mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-13 22:50:19 +08:00
fix(usage): canonicalize cached token totals
This commit is contained in:
+370
@@ -0,0 +1,370 @@
|
||||
-- Rebuild the canonical billing view so cached input is counted exactly once.
|
||||
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 split_part(lower(COALESCE(COALESCE(usage_rows.endpoint_api_format, usage_rows.api_format), '')), ':', 1)
|
||||
= 'openai'
|
||||
AND (
|
||||
GREATEST(COALESCE(usage_rows.cache_creation_input_tokens, 0), 0) > 0
|
||||
OR GREATEST(COALESCE(usage_rows.cache_creation_input_tokens_5m, 0), 0) > 0
|
||||
OR GREATEST(COALESCE(usage_rows.cache_creation_input_tokens_1h, 0), 0) > 0
|
||||
OR GREATEST(COALESCE(usage_rows.cache_read_input_tokens, 0), 0) > 0
|
||||
)
|
||||
THEN GREATEST(
|
||||
GREATEST(COALESCE(usage_rows.input_tokens, 0), 0)
|
||||
- GREATEST(
|
||||
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
|
||||
)
|
||||
- GREATEST(COALESCE(usage_rows.cache_read_input_tokens, 0), 0),
|
||||
0
|
||||
)
|
||||
WHEN split_part(lower(COALESCE(COALESCE(usage_rows.endpoint_api_format, usage_rows.api_format), '')), ':', 1)
|
||||
IN ('gemini', 'google')
|
||||
AND GREATEST(COALESCE(usage_rows.cache_read_input_tokens, 0), 0) > 0
|
||||
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 settlement.billing_cache_creation_5m_tokens IS NOT NULL
|
||||
OR settlement.billing_cache_creation_1h_tokens IS NOT NULL
|
||||
THEN COALESCE(settlement.billing_cache_creation_5m_tokens, 0)
|
||||
+ COALESCE(settlement.billing_cache_creation_1h_tokens, 0)
|
||||
END,
|
||||
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_effective_input_tokens IS NOT NULL
|
||||
THEN GREATEST(settlement.billing_effective_input_tokens, 0)
|
||||
+ GREATEST(COALESCE(settlement.billing_output_tokens, usage_rows.output_tokens, 0), 0)
|
||||
+ GREATEST(
|
||||
COALESCE(
|
||||
settlement.billing_cache_creation_tokens,
|
||||
CASE
|
||||
WHEN settlement.billing_cache_creation_5m_tokens IS NOT NULL
|
||||
OR settlement.billing_cache_creation_1h_tokens IS NOT NULL
|
||||
THEN COALESCE(settlement.billing_cache_creation_5m_tokens, 0)
|
||||
+ COALESCE(settlement.billing_cache_creation_1h_tokens, 0)
|
||||
END,
|
||||
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
|
||||
)
|
||||
+ GREATEST(
|
||||
COALESCE(
|
||||
settlement.billing_cache_read_tokens,
|
||||
usage_rows.cache_read_input_tokens,
|
||||
0
|
||||
),
|
||||
0
|
||||
)
|
||||
WHEN settlement.billing_total_input_context IS NOT NULL
|
||||
THEN GREATEST(settlement.billing_total_input_context, 0)
|
||||
+ GREATEST(COALESCE(settlement.billing_output_tokens, usage_rows.output_tokens, 0), 0)
|
||||
END,
|
||||
NULLIF(GREATEST(COALESCE(usage_rows.total_tokens, 0), 0), 0),
|
||||
CASE
|
||||
WHEN split_part(lower(COALESCE(COALESCE(usage_rows.endpoint_api_format, usage_rows.api_format), '')), ':', 1)
|
||||
IN ('openai', 'gemini', 'google')
|
||||
THEN GREATEST(COALESCE(usage_rows.input_tokens, 0), 0)
|
||||
+ GREATEST(COALESCE(usage_rows.output_tokens, 0), 0)
|
||||
ELSE GREATEST(COALESCE(usage_rows.input_tokens, 0), 0)
|
||||
+ GREATEST(COALESCE(usage_rows.output_tokens, 0), 0)
|
||||
+ GREATEST(
|
||||
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
|
||||
)
|
||||
+ GREATEST(COALESCE(usage_rows.cache_read_input_tokens, 0), 0)
|
||||
END,
|
||||
0
|
||||
),
|
||||
0
|
||||
)::bigint AS total_tokens,
|
||||
GREATEST(
|
||||
COALESCE(
|
||||
CASE
|
||||
WHEN settlement.billing_effective_input_tokens IS NOT NULL
|
||||
THEN GREATEST(settlement.billing_effective_input_tokens, 0)
|
||||
+ GREATEST(
|
||||
COALESCE(
|
||||
settlement.billing_cache_creation_tokens,
|
||||
CASE
|
||||
WHEN settlement.billing_cache_creation_5m_tokens IS NOT NULL
|
||||
OR settlement.billing_cache_creation_1h_tokens IS NOT NULL
|
||||
THEN COALESCE(settlement.billing_cache_creation_5m_tokens, 0)
|
||||
+ COALESCE(settlement.billing_cache_creation_1h_tokens, 0)
|
||||
END,
|
||||
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
|
||||
)
|
||||
+ GREATEST(
|
||||
COALESCE(
|
||||
settlement.billing_cache_read_tokens,
|
||||
usage_rows.cache_read_input_tokens,
|
||||
0
|
||||
),
|
||||
0
|
||||
)
|
||||
END,
|
||||
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)
|
||||
= 'openai'
|
||||
THEN CASE
|
||||
WHEN GREATEST(COALESCE(usage_rows.input_tokens, 0), 0) <= 0 THEN 0
|
||||
WHEN GREATEST(COALESCE(usage_rows.cache_creation_input_tokens, 0), 0) <= 0
|
||||
AND GREATEST(COALESCE(usage_rows.cache_creation_input_tokens_5m, 0), 0) <= 0
|
||||
AND GREATEST(COALESCE(usage_rows.cache_creation_input_tokens_1h, 0), 0) <= 0
|
||||
AND 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(
|
||||
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
|
||||
)
|
||||
- GREATEST(COALESCE(usage_rows.cache_read_input_tokens, 0), 0),
|
||||
0
|
||||
)
|
||||
END
|
||||
+ GREATEST(
|
||||
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
|
||||
)
|
||||
+ 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 ('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 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_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.';
|
||||
@@ -3178,7 +3178,14 @@ SELECT
|
||||
COALESCE(SUM(total_requests), 0)::BIGINT AS total_requests,
|
||||
COALESCE(SUM(input_tokens), 0)::BIGINT AS input_tokens,
|
||||
COALESCE(SUM(output_tokens), 0)::BIGINT AS output_tokens,
|
||||
COALESCE(SUM(input_tokens + output_tokens), 0)::BIGINT AS recorded_total_tokens,
|
||||
COALESCE(SUM(
|
||||
CASE
|
||||
WHEN effective_input_tokens = 0 AND total_input_context = 0 AND input_tokens > 0
|
||||
THEN input_tokens
|
||||
ELSE effective_input_tokens
|
||||
END
|
||||
+ output_tokens + cache_creation_tokens + cache_read_tokens
|
||||
), 0)::BIGINT AS recorded_total_tokens,
|
||||
COALESCE(SUM(cache_creation_tokens), 0)::BIGINT AS cache_creation_tokens,
|
||||
COALESCE(SUM(cache_creation_ephemeral_5m_tokens), 0)::BIGINT
|
||||
AS cache_creation_ephemeral_5m_tokens,
|
||||
@@ -3210,7 +3217,14 @@ SELECT
|
||||
COALESCE(SUM(total_requests), 0)::BIGINT AS total_requests,
|
||||
COALESCE(SUM(input_tokens), 0)::BIGINT AS input_tokens,
|
||||
COALESCE(SUM(output_tokens), 0)::BIGINT AS output_tokens,
|
||||
COALESCE(SUM(input_tokens + output_tokens), 0)::BIGINT AS recorded_total_tokens,
|
||||
COALESCE(SUM(
|
||||
CASE
|
||||
WHEN effective_input_tokens = 0 AND total_input_context = 0 AND input_tokens > 0
|
||||
THEN input_tokens
|
||||
ELSE effective_input_tokens
|
||||
END
|
||||
+ output_tokens + cache_creation_tokens + cache_read_tokens
|
||||
), 0)::BIGINT AS recorded_total_tokens,
|
||||
COALESCE(SUM(cache_creation_tokens), 0)::BIGINT AS cache_creation_tokens,
|
||||
COALESCE(SUM(cache_creation_ephemeral_5m_tokens), 0)::BIGINT
|
||||
AS cache_creation_ephemeral_5m_tokens,
|
||||
@@ -3257,18 +3271,8 @@ SELECT
|
||||
COALESCE(SUM(GREATEST(COALESCE("usage".input_tokens, 0), 0)), 0)::BIGINT AS input_tokens,
|
||||
COALESCE(SUM(GREATEST(COALESCE("usage".output_tokens, 0), 0)), 0)::BIGINT AS output_tokens,
|
||||
COALESCE(SUM(GREATEST(COALESCE("usage".total_tokens, 0), 0)), 0)::BIGINT AS recorded_total_tokens,
|
||||
COALESCE(SUM(
|
||||
CASE
|
||||
WHEN COALESCE("usage".cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE("usage".cache_creation_input_tokens, 0)
|
||||
END
|
||||
), 0)::BIGINT AS cache_creation_tokens,
|
||||
COALESCE(SUM(GREATEST(COALESCE("usage".cache_creation_input_tokens, 0), 0)), 0)::BIGINT
|
||||
AS cache_creation_tokens,
|
||||
COALESCE(SUM(GREATEST(COALESCE("usage".cache_creation_input_tokens_5m, 0), 0)), 0)::BIGINT
|
||||
AS cache_creation_ephemeral_5m_tokens,
|
||||
COALESCE(SUM(GREATEST(COALESCE("usage".cache_creation_input_tokens_1h, 0), 0)), 0)::BIGINT
|
||||
@@ -3945,80 +3949,10 @@ SELECT
|
||||
COALESCE(SUM(GREATEST(COALESCE("usage".input_tokens, 0), 0)), 0)::BIGINT AS input_tokens,
|
||||
COALESCE(SUM(GREATEST(COALESCE("usage".cache_read_input_tokens, 0), 0)), 0)::BIGINT
|
||||
AS cache_read_tokens,
|
||||
COALESCE(SUM(
|
||||
CASE
|
||||
WHEN COALESCE("usage".cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE("usage".cache_creation_input_tokens, 0)
|
||||
END
|
||||
), 0)::BIGINT AS cache_creation_tokens,
|
||||
COALESCE(SUM(
|
||||
CASE
|
||||
WHEN split_part(lower(COALESCE(COALESCE("usage".endpoint_api_format, "usage".api_format), '')), ':', 1)
|
||||
IN ('claude', 'anthropic')
|
||||
THEN GREATEST(COALESCE("usage".input_tokens, 0), 0)
|
||||
+ CASE
|
||||
WHEN COALESCE("usage".cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE("usage".cache_creation_input_tokens, 0)
|
||||
END
|
||||
+ GREATEST(COALESCE("usage".cache_read_input_tokens, 0), 0)
|
||||
WHEN split_part(lower(COALESCE(COALESCE("usage".endpoint_api_format, "usage".api_format), '')), ':', 1)
|
||||
IN ('openai', 'gemini', 'google')
|
||||
THEN (
|
||||
CASE
|
||||
WHEN GREATEST(COALESCE("usage".input_tokens, 0), 0) <= 0 THEN 0
|
||||
WHEN GREATEST(COALESCE("usage".cache_read_input_tokens, 0), 0) <= 0
|
||||
THEN GREATEST(COALESCE("usage".input_tokens, 0), 0)
|
||||
ELSE GREATEST(
|
||||
GREATEST(COALESCE("usage".input_tokens, 0), 0)
|
||||
- GREATEST(COALESCE("usage".cache_read_input_tokens, 0), 0),
|
||||
0
|
||||
)
|
||||
END
|
||||
) + GREATEST(COALESCE("usage".cache_read_input_tokens, 0), 0)
|
||||
ELSE CASE
|
||||
WHEN (
|
||||
CASE
|
||||
WHEN COALESCE("usage".cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE("usage".cache_creation_input_tokens, 0)
|
||||
END
|
||||
) > 0
|
||||
THEN GREATEST(COALESCE("usage".input_tokens, 0), 0)
|
||||
+ (
|
||||
CASE
|
||||
WHEN COALESCE("usage".cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE("usage".cache_creation_input_tokens, 0)
|
||||
END
|
||||
)
|
||||
+ GREATEST(COALESCE("usage".cache_read_input_tokens, 0), 0)
|
||||
ELSE GREATEST(COALESCE("usage".input_tokens, 0), 0)
|
||||
+ GREATEST(COALESCE("usage".cache_read_input_tokens, 0), 0)
|
||||
END
|
||||
END
|
||||
), 0)::BIGINT AS total_input_context,
|
||||
COALESCE(SUM(GREATEST(COALESCE("usage".cache_creation_input_tokens, 0), 0)), 0)::BIGINT
|
||||
AS cache_creation_tokens,
|
||||
COALESCE(SUM(GREATEST(COALESCE("usage".total_input_context, 0), 0)), 0)::BIGINT
|
||||
AS total_input_context,
|
||||
COALESCE(SUM(COALESCE(CAST("usage".cache_read_cost_usd AS DOUBLE PRECISION), 0)), 0)
|
||||
AS cache_read_cost_usd,
|
||||
COALESCE(SUM(COALESCE(CAST("usage".cache_creation_cost_usd AS DOUBLE PRECISION), 0)), 0)
|
||||
@@ -4887,7 +4821,7 @@ SELECT
|
||||
{group_column} AS group_key,
|
||||
COALESCE(SUM(total_requests), 0)::BIGINT AS request_count,
|
||||
COALESCE(SUM(input_tokens), 0)::BIGINT AS input_tokens,
|
||||
COALESCE(SUM(effective_input_tokens + output_tokens + cache_creation_tokens + cache_read_tokens), 0)::BIGINT AS total_tokens,
|
||||
COALESCE(SUM(total_tokens), 0)::BIGINT AS total_tokens,
|
||||
COALESCE(SUM(output_tokens), 0)::BIGINT AS output_tokens,
|
||||
COALESCE(SUM(effective_input_tokens), 0)::BIGINT AS effective_input_tokens,
|
||||
COALESCE(SUM(total_input_context), 0)::BIGINT AS total_input_context,
|
||||
@@ -4944,18 +4878,11 @@ WITH filtered_usage AS (
|
||||
SELECT
|
||||
{group_key_expr} AS group_key,
|
||||
GREATEST(COALESCE("usage".input_tokens, 0), 0) AS input_tokens,
|
||||
GREATEST(COALESCE("usage".effective_input_tokens, 0), 0) AS effective_input_tokens,
|
||||
GREATEST(COALESCE("usage".output_tokens, 0), 0) AS output_tokens,
|
||||
GREATEST(COALESCE("usage".total_tokens, 0), 0) AS total_tokens,
|
||||
CASE
|
||||
WHEN COALESCE("usage".cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE("usage".cache_creation_input_tokens, 0)
|
||||
END AS cache_creation_tokens,
|
||||
GREATEST(COALESCE("usage".total_input_context, 0), 0) AS total_input_context,
|
||||
GREATEST(COALESCE("usage".cache_creation_input_tokens, 0), 0) AS cache_creation_tokens,
|
||||
GREATEST(COALESCE("usage".cache_creation_input_tokens_5m, 0), 0)
|
||||
AS cache_creation_ephemeral_5m_tokens,
|
||||
GREATEST(COALESCE("usage".cache_creation_input_tokens_1h, 0), 0)
|
||||
@@ -4995,8 +4922,7 @@ WITH filtered_usage AS (
|
||||
AND "usage".response_time_ms IS NOT NULL
|
||||
THEN 1
|
||||
ELSE 0
|
||||
END AS successful_response_time_samples,
|
||||
COALESCE(COALESCE("usage".endpoint_api_format, "usage".api_format), '') AS normalized_api_format
|
||||
END AS successful_response_time_samples
|
||||
FROM usage_billing_facts AS "usage"
|
||||
"#,
|
||||
));
|
||||
@@ -5054,8 +4980,10 @@ normalized_usage AS (
|
||||
SELECT
|
||||
group_key,
|
||||
input_tokens,
|
||||
effective_input_tokens,
|
||||
total_tokens,
|
||||
output_tokens,
|
||||
total_input_context,
|
||||
cache_creation_tokens,
|
||||
cache_creation_ephemeral_5m_tokens,
|
||||
cache_creation_ephemeral_1h_tokens,
|
||||
@@ -5066,49 +4994,7 @@ normalized_usage AS (
|
||||
response_time_ms,
|
||||
response_time_samples,
|
||||
successful_response_time_ms,
|
||||
successful_response_time_samples,
|
||||
CASE
|
||||
WHEN input_tokens <= 0 THEN 0
|
||||
WHEN split_part(lower(COALESCE(normalized_api_format, '')), ':', 1)
|
||||
= 'openai'
|
||||
AND (cache_creation_tokens > 0 OR cache_read_tokens > 0)
|
||||
THEN GREATEST(input_tokens - cache_creation_tokens - cache_read_tokens, 0)
|
||||
WHEN split_part(lower(COALESCE(normalized_api_format, '')), ':', 1)
|
||||
IN ('gemini', 'google')
|
||||
AND cache_read_tokens > 0
|
||||
THEN GREATEST(input_tokens - cache_read_tokens, 0)
|
||||
ELSE input_tokens
|
||||
END AS effective_input_tokens,
|
||||
CASE
|
||||
WHEN split_part(lower(COALESCE(normalized_api_format, '')), ':', 1)
|
||||
IN ('claude', 'anthropic')
|
||||
THEN input_tokens + cache_creation_tokens + cache_read_tokens
|
||||
WHEN split_part(lower(COALESCE(normalized_api_format, '')), ':', 1)
|
||||
= 'openai'
|
||||
THEN (
|
||||
CASE
|
||||
WHEN input_tokens <= 0 THEN 0
|
||||
WHEN cache_creation_tokens > 0 OR cache_read_tokens > 0
|
||||
THEN GREATEST(input_tokens - cache_creation_tokens - cache_read_tokens, 0)
|
||||
ELSE input_tokens
|
||||
END
|
||||
) + cache_creation_tokens + cache_read_tokens
|
||||
WHEN split_part(lower(COALESCE(normalized_api_format, '')), ':', 1)
|
||||
IN ('gemini', 'google')
|
||||
THEN (
|
||||
CASE
|
||||
WHEN input_tokens <= 0 THEN 0
|
||||
WHEN cache_read_tokens > 0
|
||||
THEN GREATEST(input_tokens - cache_read_tokens, 0)
|
||||
ELSE input_tokens
|
||||
END
|
||||
) + cache_read_tokens
|
||||
ELSE CASE
|
||||
WHEN cache_creation_tokens > 0
|
||||
THEN input_tokens + cache_creation_tokens + cache_read_tokens
|
||||
ELSE input_tokens + cache_read_tokens
|
||||
END
|
||||
END AS total_input_context
|
||||
successful_response_time_samples
|
||||
FROM filtered_usage
|
||||
)
|
||||
SELECT
|
||||
@@ -6801,33 +6687,7 @@ SELECT
|
||||
{group_key_expr} AS group_key,
|
||||
MAX({legacy_name_expr}) AS legacy_name,
|
||||
COUNT(*)::BIGINT AS request_count,
|
||||
COALESCE(SUM(
|
||||
CASE
|
||||
WHEN GREATEST(COALESCE("usage".input_tokens, 0), 0) <= 0 THEN 0
|
||||
WHEN GREATEST(COALESCE("usage".cache_read_input_tokens, 0), 0) <= 0
|
||||
THEN GREATEST(COALESCE("usage".input_tokens, 0), 0)
|
||||
WHEN split_part(lower(COALESCE(COALESCE("usage".endpoint_api_format, "usage".api_format), '')), ':', 1)
|
||||
IN ('openai', 'gemini', 'google')
|
||||
THEN GREATEST(
|
||||
GREATEST(COALESCE("usage".input_tokens, 0), 0)
|
||||
- GREATEST(COALESCE("usage".cache_read_input_tokens, 0), 0),
|
||||
0
|
||||
)
|
||||
ELSE GREATEST(COALESCE("usage".input_tokens, 0), 0)
|
||||
END
|
||||
+ GREATEST(COALESCE("usage".output_tokens, 0), 0)
|
||||
+ CASE
|
||||
WHEN COALESCE("usage".cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE("usage".cache_creation_input_tokens, 0)
|
||||
END
|
||||
+ GREATEST(COALESCE("usage".cache_read_input_tokens, 0), 0)
|
||||
), 0)::BIGINT AS total_tokens,
|
||||
COALESCE(SUM(GREATEST(COALESCE("usage".total_tokens, 0), 0)), 0)::BIGINT AS total_tokens,
|
||||
COALESCE(SUM(COALESCE(CAST("usage".total_cost_usd AS DOUBLE PRECISION), 0)), 0)
|
||||
AS total_cost_usd
|
||||
FROM usage_billing_facts AS "usage"
|
||||
@@ -6983,10 +6843,7 @@ SELECT
|
||||
user_id AS group_key,
|
||||
MAX(NULLIF(BTRIM(username), '')) AS legacy_name,
|
||||
COALESCE(SUM(total_requests), 0)::BIGINT AS request_count,
|
||||
COALESCE(
|
||||
SUM(effective_input_tokens + output_tokens + cache_creation_tokens + cache_read_tokens),
|
||||
0
|
||||
)::BIGINT AS total_tokens,
|
||||
COALESCE(SUM(total_tokens), 0)::BIGINT AS total_tokens,
|
||||
CAST(COALESCE(SUM(total_cost), 0) AS DOUBLE PRECISION) AS total_cost_usd
|
||||
FROM stats_user_daily_provider
|
||||
WHERE date >=
|
||||
@@ -7012,10 +6869,7 @@ SELECT
|
||||
user_id AS group_key,
|
||||
MAX(NULLIF(BTRIM(username), '')) AS legacy_name,
|
||||
COALESCE(SUM(total_requests), 0)::BIGINT AS request_count,
|
||||
COALESCE(
|
||||
SUM(effective_input_tokens + output_tokens + cache_creation_tokens + cache_read_tokens),
|
||||
0
|
||||
)::BIGINT AS total_tokens,
|
||||
COALESCE(SUM(total_tokens), 0)::BIGINT AS total_tokens,
|
||||
CAST(COALESCE(SUM(total_cost), 0) AS DOUBLE PRECISION) AS total_cost_usd
|
||||
FROM stats_user_daily_model
|
||||
WHERE date >=
|
||||
@@ -7300,25 +7154,16 @@ WITH filtered_usage AS (
|
||||
{provider_display_name_expr} AS provider_display_name,
|
||||
{provider_identity_source_expr} AS provider_identity_source,
|
||||
COALESCE("usage".api_format, 'unknown') AS api_format_group_key,
|
||||
GREATEST(COALESCE("usage".input_tokens, 0), 0) AS input_tokens,
|
||||
GREATEST(COALESCE("usage".effective_input_tokens, 0), 0) AS effective_input_tokens,
|
||||
GREATEST(COALESCE("usage".output_tokens, 0), 0) AS output_tokens,
|
||||
GREATEST(COALESCE("usage".total_tokens, 0), 0) AS total_tokens,
|
||||
CASE
|
||||
WHEN COALESCE("usage".cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE("usage".cache_creation_input_tokens, 0)
|
||||
END AS cache_creation_tokens,
|
||||
GREATEST(COALESCE("usage".total_input_context, 0), 0) AS total_input_context,
|
||||
GREATEST(COALESCE("usage".cache_creation_input_tokens, 0), 0) AS cache_creation_tokens,
|
||||
GREATEST(COALESCE("usage".cache_creation_input_tokens_5m, 0), 0)
|
||||
AS cache_creation_ephemeral_5m_tokens,
|
||||
GREATEST(COALESCE("usage".cache_creation_input_tokens_1h, 0), 0)
|
||||
AS cache_creation_ephemeral_1h_tokens,
|
||||
GREATEST(COALESCE("usage".cache_read_input_tokens, 0), 0) AS cache_read_tokens,
|
||||
COALESCE("usage".endpoint_api_format, "usage".api_format) AS normalized_api_format,
|
||||
COALESCE(CAST("usage".total_cost_usd AS DOUBLE PRECISION), 0) AS total_cost_usd,
|
||||
COALESCE(CAST("usage".actual_total_cost_usd AS DOUBLE PRECISION), 0) AS actual_total_cost_usd,
|
||||
GREATEST(COALESCE("usage".response_time_ms, 0), 0) AS response_time_ms,
|
||||
@@ -7343,6 +7188,8 @@ normalized_usage AS (
|
||||
{secondary_name_expr} AS secondary_name,
|
||||
total_tokens,
|
||||
output_tokens,
|
||||
effective_input_tokens,
|
||||
total_input_context,
|
||||
cache_creation_tokens,
|
||||
cache_creation_ephemeral_5m_tokens,
|
||||
cache_creation_ephemeral_1h_tokens,
|
||||
@@ -7350,49 +7197,7 @@ normalized_usage AS (
|
||||
total_cost_usd,
|
||||
actual_total_cost_usd,
|
||||
response_time_ms,
|
||||
success_flag,
|
||||
CASE
|
||||
WHEN input_tokens <= 0 THEN 0
|
||||
WHEN split_part(lower(COALESCE(normalized_api_format, '')), ':', 1)
|
||||
= 'openai'
|
||||
AND (cache_creation_tokens > 0 OR cache_read_tokens > 0)
|
||||
THEN GREATEST(input_tokens - cache_creation_tokens - cache_read_tokens, 0)
|
||||
WHEN split_part(lower(COALESCE(normalized_api_format, '')), ':', 1)
|
||||
IN ('gemini', 'google')
|
||||
AND cache_read_tokens > 0
|
||||
THEN GREATEST(input_tokens - cache_read_tokens, 0)
|
||||
ELSE input_tokens
|
||||
END AS effective_input_tokens,
|
||||
CASE
|
||||
WHEN split_part(lower(COALESCE(normalized_api_format, '')), ':', 1)
|
||||
IN ('claude', 'anthropic')
|
||||
THEN input_tokens + cache_creation_tokens + cache_read_tokens
|
||||
WHEN split_part(lower(COALESCE(normalized_api_format, '')), ':', 1)
|
||||
= 'openai'
|
||||
THEN (
|
||||
CASE
|
||||
WHEN input_tokens <= 0 THEN 0
|
||||
WHEN cache_creation_tokens > 0 OR cache_read_tokens > 0
|
||||
THEN GREATEST(input_tokens - cache_creation_tokens - cache_read_tokens, 0)
|
||||
ELSE input_tokens
|
||||
END
|
||||
) + cache_creation_tokens + cache_read_tokens
|
||||
WHEN split_part(lower(COALESCE(normalized_api_format, '')), ':', 1)
|
||||
IN ('gemini', 'google')
|
||||
THEN (
|
||||
CASE
|
||||
WHEN input_tokens <= 0 THEN 0
|
||||
WHEN cache_read_tokens > 0
|
||||
THEN GREATEST(input_tokens - cache_read_tokens, 0)
|
||||
ELSE input_tokens
|
||||
END
|
||||
) + cache_read_tokens
|
||||
ELSE CASE
|
||||
WHEN cache_creation_tokens > 0
|
||||
THEN input_tokens + cache_creation_tokens + cache_read_tokens
|
||||
ELSE input_tokens + cache_read_tokens
|
||||
END
|
||||
END AS total_input_context
|
||||
success_flag
|
||||
FROM filtered_usage
|
||||
),
|
||||
aggregated_usage AS (
|
||||
@@ -7552,14 +7357,7 @@ LIMIT $3
|
||||
r#"SELECT
|
||||
DATE("usage".created_at) AS day,
|
||||
COUNT(*)::BIGINT AS requests,
|
||||
COALESCE(SUM("usage".input_tokens + "usage".output_tokens
|
||||
+ CASE
|
||||
WHEN COALESCE("usage".cache_creation_input_tokens, 0) = 0
|
||||
AND (COALESCE("usage".cache_creation_input_tokens_5m, 0) + COALESCE("usage".cache_creation_input_tokens_1h, 0)) > 0
|
||||
THEN COALESCE("usage".cache_creation_input_tokens_5m, 0) + COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE("usage".cache_creation_input_tokens, 0)
|
||||
END
|
||||
+ COALESCE("usage".cache_read_input_tokens, 0)), 0)::BIGINT AS total_tokens,
|
||||
COALESCE(SUM(GREATEST(COALESCE("usage".total_tokens, 0), 0)), 0)::BIGINT AS total_tokens,
|
||||
COALESCE(SUM(CAST("usage".total_cost_usd AS DOUBLE PRECISION)), 0) AS total_cost_usd,
|
||||
COALESCE(SUM(CAST("usage".actual_total_cost_usd AS DOUBLE PRECISION)), 0) AS actual_total_cost_usd
|
||||
FROM usage_billing_facts AS "usage"
|
||||
@@ -7803,14 +7601,9 @@ ORDER BY api_key_id ASC
|
||||
SELECT
|
||||
api_key_id,
|
||||
COALESCE(
|
||||
SUM(
|
||||
COALESCE(
|
||||
total_tokens,
|
||||
COALESCE(input_tokens, 0) + COALESCE(output_tokens, 0)
|
||||
)
|
||||
),
|
||||
SUM(GREATEST(COALESCE(total_tokens, 0), 0)),
|
||||
0
|
||||
) AS total_tokens
|
||||
)::BIGINT AS total_tokens
|
||||
FROM usage_billing_facts AS "usage"
|
||||
WHERE api_key_id = ANY(
|
||||
"#,
|
||||
@@ -11281,9 +11074,12 @@ fn usage_total_input_context(
|
||||
.saturating_add(cache_creation_tokens)
|
||||
.saturating_add(cache_read_tokens),
|
||||
),
|
||||
"openai" | "gemini" | "google" => {
|
||||
Some(effective_input_tokens.saturating_add(cache_read_tokens))
|
||||
}
|
||||
"openai" => Some(
|
||||
effective_input_tokens
|
||||
.saturating_add(cache_creation_tokens)
|
||||
.saturating_add(cache_read_tokens),
|
||||
),
|
||||
"gemini" | "google" => Some(effective_input_tokens.saturating_add(cache_read_tokens)),
|
||||
_ => Some(
|
||||
input_tokens
|
||||
.saturating_add(cache_creation_tokens)
|
||||
|
||||
@@ -24,27 +24,92 @@ SELECT
|
||||
CAST(COALESCE(usage_settlement_snapshots.billing_output_tokens, "usage".output_tokens) AS INTEGER) AS output_tokens,
|
||||
CAST(COALESCE(
|
||||
CASE
|
||||
WHEN usage_settlement_snapshots.billing_input_tokens IS NOT NULL
|
||||
OR usage_settlement_snapshots.billing_output_tokens IS NOT NULL
|
||||
OR usage_settlement_snapshots.billing_cache_creation_tokens IS NOT NULL
|
||||
OR usage_settlement_snapshots.billing_cache_creation_5m_tokens IS NOT NULL
|
||||
OR usage_settlement_snapshots.billing_cache_creation_1h_tokens IS NOT NULL
|
||||
OR usage_settlement_snapshots.billing_cache_read_tokens IS NOT NULL
|
||||
THEN COALESCE(usage_settlement_snapshots.billing_input_tokens, 0)
|
||||
+ COALESCE(usage_settlement_snapshots.billing_output_tokens, 0)
|
||||
+ COALESCE(
|
||||
usage_settlement_snapshots.billing_cache_creation_tokens,
|
||||
COALESCE(usage_settlement_snapshots.billing_cache_creation_5m_tokens, 0)
|
||||
+ COALESCE(usage_settlement_snapshots.billing_cache_creation_1h_tokens, 0),
|
||||
WHEN usage_settlement_snapshots.billing_effective_input_tokens IS NOT NULL
|
||||
THEN GREATEST(usage_settlement_snapshots.billing_effective_input_tokens, 0)
|
||||
+ GREATEST(
|
||||
COALESCE(usage_settlement_snapshots.billing_output_tokens, "usage".output_tokens, 0),
|
||||
0
|
||||
)
|
||||
+ GREATEST(
|
||||
COALESCE(
|
||||
usage_settlement_snapshots.billing_cache_creation_tokens,
|
||||
CASE
|
||||
WHEN usage_settlement_snapshots.billing_cache_creation_5m_tokens IS NOT NULL
|
||||
OR usage_settlement_snapshots.billing_cache_creation_1h_tokens IS NOT NULL
|
||||
THEN COALESCE(usage_settlement_snapshots.billing_cache_creation_5m_tokens, 0)
|
||||
+ COALESCE(usage_settlement_snapshots.billing_cache_creation_1h_tokens, 0)
|
||||
END,
|
||||
CASE
|
||||
WHEN COALESCE("usage".cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE("usage".cache_creation_input_tokens, 0)
|
||||
END,
|
||||
0
|
||||
),
|
||||
0
|
||||
)
|
||||
+ GREATEST(
|
||||
COALESCE(
|
||||
usage_settlement_snapshots.billing_cache_read_tokens,
|
||||
"usage".cache_read_input_tokens,
|
||||
0
|
||||
),
|
||||
0
|
||||
)
|
||||
WHEN usage_settlement_snapshots.billing_total_input_context IS NOT NULL
|
||||
THEN GREATEST(usage_settlement_snapshots.billing_total_input_context, 0)
|
||||
+ GREATEST(
|
||||
COALESCE(usage_settlement_snapshots.billing_output_tokens, "usage".output_tokens, 0),
|
||||
0
|
||||
)
|
||||
+ COALESCE(usage_settlement_snapshots.billing_cache_read_tokens, 0)
|
||||
END,
|
||||
"usage".total_tokens
|
||||
NULLIF(GREATEST(COALESCE("usage".total_tokens, 0), 0), 0),
|
||||
CASE
|
||||
WHEN split_part(lower(COALESCE(COALESCE("usage".endpoint_api_format, "usage".api_format), '')), ':', 1)
|
||||
IN ('openai', 'gemini', 'google')
|
||||
THEN GREATEST(COALESCE("usage".input_tokens, 0), 0)
|
||||
+ GREATEST(COALESCE("usage".output_tokens, 0), 0)
|
||||
ELSE GREATEST(COALESCE("usage".input_tokens, 0), 0)
|
||||
+ GREATEST(COALESCE("usage".output_tokens, 0), 0)
|
||||
+ GREATEST(
|
||||
CASE
|
||||
WHEN COALESCE("usage".cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE("usage".cache_creation_input_tokens, 0)
|
||||
END,
|
||||
0
|
||||
)
|
||||
+ GREATEST(COALESCE("usage".cache_read_input_tokens, 0), 0)
|
||||
END
|
||||
) AS INTEGER) AS total_tokens,
|
||||
CAST(COALESCE(
|
||||
usage_settlement_snapshots.billing_cache_creation_tokens,
|
||||
"usage".cache_creation_input_tokens,
|
||||
CASE
|
||||
WHEN usage_settlement_snapshots.billing_cache_creation_5m_tokens IS NOT NULL
|
||||
OR usage_settlement_snapshots.billing_cache_creation_1h_tokens IS NOT NULL
|
||||
THEN COALESCE(usage_settlement_snapshots.billing_cache_creation_5m_tokens, 0)
|
||||
+ COALESCE(usage_settlement_snapshots.billing_cache_creation_1h_tokens, 0)
|
||||
END,
|
||||
CASE
|
||||
WHEN COALESCE("usage".cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE("usage".cache_creation_input_tokens, 0)
|
||||
END,
|
||||
0
|
||||
) AS INTEGER) AS cache_creation_input_tokens,
|
||||
CAST(COALESCE(
|
||||
|
||||
@@ -24,27 +24,92 @@ SELECT
|
||||
CAST(COALESCE(usage_settlement_snapshots.billing_output_tokens, "usage".output_tokens) AS INTEGER) AS output_tokens,
|
||||
CAST(COALESCE(
|
||||
CASE
|
||||
WHEN usage_settlement_snapshots.billing_input_tokens IS NOT NULL
|
||||
OR usage_settlement_snapshots.billing_output_tokens IS NOT NULL
|
||||
OR usage_settlement_snapshots.billing_cache_creation_tokens IS NOT NULL
|
||||
OR usage_settlement_snapshots.billing_cache_creation_5m_tokens IS NOT NULL
|
||||
OR usage_settlement_snapshots.billing_cache_creation_1h_tokens IS NOT NULL
|
||||
OR usage_settlement_snapshots.billing_cache_read_tokens IS NOT NULL
|
||||
THEN COALESCE(usage_settlement_snapshots.billing_input_tokens, 0)
|
||||
+ COALESCE(usage_settlement_snapshots.billing_output_tokens, 0)
|
||||
+ COALESCE(
|
||||
usage_settlement_snapshots.billing_cache_creation_tokens,
|
||||
COALESCE(usage_settlement_snapshots.billing_cache_creation_5m_tokens, 0)
|
||||
+ COALESCE(usage_settlement_snapshots.billing_cache_creation_1h_tokens, 0),
|
||||
WHEN usage_settlement_snapshots.billing_effective_input_tokens IS NOT NULL
|
||||
THEN GREATEST(usage_settlement_snapshots.billing_effective_input_tokens, 0)
|
||||
+ GREATEST(
|
||||
COALESCE(usage_settlement_snapshots.billing_output_tokens, "usage".output_tokens, 0),
|
||||
0
|
||||
)
|
||||
+ GREATEST(
|
||||
COALESCE(
|
||||
usage_settlement_snapshots.billing_cache_creation_tokens,
|
||||
CASE
|
||||
WHEN usage_settlement_snapshots.billing_cache_creation_5m_tokens IS NOT NULL
|
||||
OR usage_settlement_snapshots.billing_cache_creation_1h_tokens IS NOT NULL
|
||||
THEN COALESCE(usage_settlement_snapshots.billing_cache_creation_5m_tokens, 0)
|
||||
+ COALESCE(usage_settlement_snapshots.billing_cache_creation_1h_tokens, 0)
|
||||
END,
|
||||
CASE
|
||||
WHEN COALESCE("usage".cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE("usage".cache_creation_input_tokens, 0)
|
||||
END,
|
||||
0
|
||||
),
|
||||
0
|
||||
)
|
||||
+ GREATEST(
|
||||
COALESCE(
|
||||
usage_settlement_snapshots.billing_cache_read_tokens,
|
||||
"usage".cache_read_input_tokens,
|
||||
0
|
||||
),
|
||||
0
|
||||
)
|
||||
WHEN usage_settlement_snapshots.billing_total_input_context IS NOT NULL
|
||||
THEN GREATEST(usage_settlement_snapshots.billing_total_input_context, 0)
|
||||
+ GREATEST(
|
||||
COALESCE(usage_settlement_snapshots.billing_output_tokens, "usage".output_tokens, 0),
|
||||
0
|
||||
)
|
||||
+ COALESCE(usage_settlement_snapshots.billing_cache_read_tokens, 0)
|
||||
END,
|
||||
"usage".total_tokens
|
||||
NULLIF(GREATEST(COALESCE("usage".total_tokens, 0), 0), 0),
|
||||
CASE
|
||||
WHEN split_part(lower(COALESCE(COALESCE("usage".endpoint_api_format, "usage".api_format), '')), ':', 1)
|
||||
IN ('openai', 'gemini', 'google')
|
||||
THEN GREATEST(COALESCE("usage".input_tokens, 0), 0)
|
||||
+ GREATEST(COALESCE("usage".output_tokens, 0), 0)
|
||||
ELSE GREATEST(COALESCE("usage".input_tokens, 0), 0)
|
||||
+ GREATEST(COALESCE("usage".output_tokens, 0), 0)
|
||||
+ GREATEST(
|
||||
CASE
|
||||
WHEN COALESCE("usage".cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE("usage".cache_creation_input_tokens, 0)
|
||||
END,
|
||||
0
|
||||
)
|
||||
+ GREATEST(COALESCE("usage".cache_read_input_tokens, 0), 0)
|
||||
END
|
||||
) AS INTEGER) AS total_tokens,
|
||||
CAST(COALESCE(
|
||||
usage_settlement_snapshots.billing_cache_creation_tokens,
|
||||
"usage".cache_creation_input_tokens,
|
||||
CASE
|
||||
WHEN usage_settlement_snapshots.billing_cache_creation_5m_tokens IS NOT NULL
|
||||
OR usage_settlement_snapshots.billing_cache_creation_1h_tokens IS NOT NULL
|
||||
THEN COALESCE(usage_settlement_snapshots.billing_cache_creation_5m_tokens, 0)
|
||||
+ COALESCE(usage_settlement_snapshots.billing_cache_creation_1h_tokens, 0)
|
||||
END,
|
||||
CASE
|
||||
WHEN COALESCE("usage".cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE("usage".cache_creation_input_tokens, 0)
|
||||
END,
|
||||
0
|
||||
) AS INTEGER) AS cache_creation_input_tokens,
|
||||
CAST(COALESCE(
|
||||
|
||||
+80
-15
@@ -24,27 +24,92 @@ SELECT
|
||||
CAST(COALESCE(usage_settlement_snapshots.billing_output_tokens, "usage".output_tokens) AS INTEGER) AS output_tokens,
|
||||
CAST(COALESCE(
|
||||
CASE
|
||||
WHEN usage_settlement_snapshots.billing_input_tokens IS NOT NULL
|
||||
OR usage_settlement_snapshots.billing_output_tokens IS NOT NULL
|
||||
OR usage_settlement_snapshots.billing_cache_creation_tokens IS NOT NULL
|
||||
OR usage_settlement_snapshots.billing_cache_creation_5m_tokens IS NOT NULL
|
||||
OR usage_settlement_snapshots.billing_cache_creation_1h_tokens IS NOT NULL
|
||||
OR usage_settlement_snapshots.billing_cache_read_tokens IS NOT NULL
|
||||
THEN COALESCE(usage_settlement_snapshots.billing_input_tokens, 0)
|
||||
+ COALESCE(usage_settlement_snapshots.billing_output_tokens, 0)
|
||||
+ COALESCE(
|
||||
usage_settlement_snapshots.billing_cache_creation_tokens,
|
||||
COALESCE(usage_settlement_snapshots.billing_cache_creation_5m_tokens, 0)
|
||||
+ COALESCE(usage_settlement_snapshots.billing_cache_creation_1h_tokens, 0),
|
||||
WHEN usage_settlement_snapshots.billing_effective_input_tokens IS NOT NULL
|
||||
THEN GREATEST(usage_settlement_snapshots.billing_effective_input_tokens, 0)
|
||||
+ GREATEST(
|
||||
COALESCE(usage_settlement_snapshots.billing_output_tokens, "usage".output_tokens, 0),
|
||||
0
|
||||
)
|
||||
+ GREATEST(
|
||||
COALESCE(
|
||||
usage_settlement_snapshots.billing_cache_creation_tokens,
|
||||
CASE
|
||||
WHEN usage_settlement_snapshots.billing_cache_creation_5m_tokens IS NOT NULL
|
||||
OR usage_settlement_snapshots.billing_cache_creation_1h_tokens IS NOT NULL
|
||||
THEN COALESCE(usage_settlement_snapshots.billing_cache_creation_5m_tokens, 0)
|
||||
+ COALESCE(usage_settlement_snapshots.billing_cache_creation_1h_tokens, 0)
|
||||
END,
|
||||
CASE
|
||||
WHEN COALESCE("usage".cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE("usage".cache_creation_input_tokens, 0)
|
||||
END,
|
||||
0
|
||||
),
|
||||
0
|
||||
)
|
||||
+ GREATEST(
|
||||
COALESCE(
|
||||
usage_settlement_snapshots.billing_cache_read_tokens,
|
||||
"usage".cache_read_input_tokens,
|
||||
0
|
||||
),
|
||||
0
|
||||
)
|
||||
WHEN usage_settlement_snapshots.billing_total_input_context IS NOT NULL
|
||||
THEN GREATEST(usage_settlement_snapshots.billing_total_input_context, 0)
|
||||
+ GREATEST(
|
||||
COALESCE(usage_settlement_snapshots.billing_output_tokens, "usage".output_tokens, 0),
|
||||
0
|
||||
)
|
||||
+ COALESCE(usage_settlement_snapshots.billing_cache_read_tokens, 0)
|
||||
END,
|
||||
"usage".total_tokens
|
||||
NULLIF(GREATEST(COALESCE("usage".total_tokens, 0), 0), 0),
|
||||
CASE
|
||||
WHEN split_part(lower(COALESCE(COALESCE("usage".endpoint_api_format, "usage".api_format), '')), ':', 1)
|
||||
IN ('openai', 'gemini', 'google')
|
||||
THEN GREATEST(COALESCE("usage".input_tokens, 0), 0)
|
||||
+ GREATEST(COALESCE("usage".output_tokens, 0), 0)
|
||||
ELSE GREATEST(COALESCE("usage".input_tokens, 0), 0)
|
||||
+ GREATEST(COALESCE("usage".output_tokens, 0), 0)
|
||||
+ GREATEST(
|
||||
CASE
|
||||
WHEN COALESCE("usage".cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE("usage".cache_creation_input_tokens, 0)
|
||||
END,
|
||||
0
|
||||
)
|
||||
+ GREATEST(COALESCE("usage".cache_read_input_tokens, 0), 0)
|
||||
END
|
||||
) AS INTEGER) AS total_tokens,
|
||||
CAST(COALESCE(
|
||||
usage_settlement_snapshots.billing_cache_creation_tokens,
|
||||
"usage".cache_creation_input_tokens,
|
||||
CASE
|
||||
WHEN usage_settlement_snapshots.billing_cache_creation_5m_tokens IS NOT NULL
|
||||
OR usage_settlement_snapshots.billing_cache_creation_1h_tokens IS NOT NULL
|
||||
THEN COALESCE(usage_settlement_snapshots.billing_cache_creation_5m_tokens, 0)
|
||||
+ COALESCE(usage_settlement_snapshots.billing_cache_creation_1h_tokens, 0)
|
||||
END,
|
||||
CASE
|
||||
WHEN COALESCE("usage".cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE("usage".cache_creation_input_tokens, 0)
|
||||
END,
|
||||
0
|
||||
) AS INTEGER) AS cache_creation_input_tokens,
|
||||
CAST(COALESCE(
|
||||
|
||||
+80
-15
@@ -24,27 +24,92 @@ SELECT
|
||||
CAST(COALESCE(usage_settlement_snapshots.billing_output_tokens, "usage".output_tokens) AS INTEGER) AS output_tokens,
|
||||
CAST(COALESCE(
|
||||
CASE
|
||||
WHEN usage_settlement_snapshots.billing_input_tokens IS NOT NULL
|
||||
OR usage_settlement_snapshots.billing_output_tokens IS NOT NULL
|
||||
OR usage_settlement_snapshots.billing_cache_creation_tokens IS NOT NULL
|
||||
OR usage_settlement_snapshots.billing_cache_creation_5m_tokens IS NOT NULL
|
||||
OR usage_settlement_snapshots.billing_cache_creation_1h_tokens IS NOT NULL
|
||||
OR usage_settlement_snapshots.billing_cache_read_tokens IS NOT NULL
|
||||
THEN COALESCE(usage_settlement_snapshots.billing_input_tokens, 0)
|
||||
+ COALESCE(usage_settlement_snapshots.billing_output_tokens, 0)
|
||||
+ COALESCE(
|
||||
usage_settlement_snapshots.billing_cache_creation_tokens,
|
||||
COALESCE(usage_settlement_snapshots.billing_cache_creation_5m_tokens, 0)
|
||||
+ COALESCE(usage_settlement_snapshots.billing_cache_creation_1h_tokens, 0),
|
||||
WHEN usage_settlement_snapshots.billing_effective_input_tokens IS NOT NULL
|
||||
THEN GREATEST(usage_settlement_snapshots.billing_effective_input_tokens, 0)
|
||||
+ GREATEST(
|
||||
COALESCE(usage_settlement_snapshots.billing_output_tokens, "usage".output_tokens, 0),
|
||||
0
|
||||
)
|
||||
+ GREATEST(
|
||||
COALESCE(
|
||||
usage_settlement_snapshots.billing_cache_creation_tokens,
|
||||
CASE
|
||||
WHEN usage_settlement_snapshots.billing_cache_creation_5m_tokens IS NOT NULL
|
||||
OR usage_settlement_snapshots.billing_cache_creation_1h_tokens IS NOT NULL
|
||||
THEN COALESCE(usage_settlement_snapshots.billing_cache_creation_5m_tokens, 0)
|
||||
+ COALESCE(usage_settlement_snapshots.billing_cache_creation_1h_tokens, 0)
|
||||
END,
|
||||
CASE
|
||||
WHEN COALESCE("usage".cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE("usage".cache_creation_input_tokens, 0)
|
||||
END,
|
||||
0
|
||||
),
|
||||
0
|
||||
)
|
||||
+ GREATEST(
|
||||
COALESCE(
|
||||
usage_settlement_snapshots.billing_cache_read_tokens,
|
||||
"usage".cache_read_input_tokens,
|
||||
0
|
||||
),
|
||||
0
|
||||
)
|
||||
WHEN usage_settlement_snapshots.billing_total_input_context IS NOT NULL
|
||||
THEN GREATEST(usage_settlement_snapshots.billing_total_input_context, 0)
|
||||
+ GREATEST(
|
||||
COALESCE(usage_settlement_snapshots.billing_output_tokens, "usage".output_tokens, 0),
|
||||
0
|
||||
)
|
||||
+ COALESCE(usage_settlement_snapshots.billing_cache_read_tokens, 0)
|
||||
END,
|
||||
"usage".total_tokens
|
||||
NULLIF(GREATEST(COALESCE("usage".total_tokens, 0), 0), 0),
|
||||
CASE
|
||||
WHEN split_part(lower(COALESCE(COALESCE("usage".endpoint_api_format, "usage".api_format), '')), ':', 1)
|
||||
IN ('openai', 'gemini', 'google')
|
||||
THEN GREATEST(COALESCE("usage".input_tokens, 0), 0)
|
||||
+ GREATEST(COALESCE("usage".output_tokens, 0), 0)
|
||||
ELSE GREATEST(COALESCE("usage".input_tokens, 0), 0)
|
||||
+ GREATEST(COALESCE("usage".output_tokens, 0), 0)
|
||||
+ GREATEST(
|
||||
CASE
|
||||
WHEN COALESCE("usage".cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE("usage".cache_creation_input_tokens, 0)
|
||||
END,
|
||||
0
|
||||
)
|
||||
+ GREATEST(COALESCE("usage".cache_read_input_tokens, 0), 0)
|
||||
END
|
||||
) AS INTEGER) AS total_tokens,
|
||||
CAST(COALESCE(
|
||||
usage_settlement_snapshots.billing_cache_creation_tokens,
|
||||
"usage".cache_creation_input_tokens,
|
||||
CASE
|
||||
WHEN usage_settlement_snapshots.billing_cache_creation_5m_tokens IS NOT NULL
|
||||
OR usage_settlement_snapshots.billing_cache_creation_1h_tokens IS NOT NULL
|
||||
THEN COALESCE(usage_settlement_snapshots.billing_cache_creation_5m_tokens, 0)
|
||||
+ COALESCE(usage_settlement_snapshots.billing_cache_creation_1h_tokens, 0)
|
||||
END,
|
||||
CASE
|
||||
WHEN COALESCE("usage".cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE("usage".cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE("usage".cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE("usage".cache_creation_input_tokens, 0)
|
||||
END,
|
||||
0
|
||||
) AS INTEGER) AS cache_creation_input_tokens,
|
||||
CAST(COALESCE(
|
||||
|
||||
+1
-4
@@ -4,10 +4,7 @@ WITH aggregated AS (
|
||||
COUNT(*)::BIGINT AS total_requests,
|
||||
COALESCE(SUM(
|
||||
GREATEST(
|
||||
COALESCE(
|
||||
total_tokens,
|
||||
COALESCE(input_tokens, 0) + COALESCE(output_tokens, 0)
|
||||
),
|
||||
COALESCE(total_tokens, 0),
|
||||
0
|
||||
)::BIGINT
|
||||
), 0)::BIGINT AS total_tokens,
|
||||
|
||||
+1
-4
@@ -27,10 +27,7 @@ WITH aggregated AS (
|
||||
CASE
|
||||
WHEN status IN ('pending', 'streaming') THEN 0
|
||||
ELSE GREATEST(
|
||||
COALESCE(
|
||||
total_tokens,
|
||||
COALESCE(input_tokens, 0) + COALESCE(output_tokens, 0)
|
||||
),
|
||||
COALESCE(total_tokens, 0),
|
||||
0
|
||||
)::BIGINT
|
||||
END
|
||||
|
||||
+2
-5
@@ -2,13 +2,10 @@ SELECT
|
||||
api_key_id,
|
||||
COALESCE(
|
||||
SUM(
|
||||
COALESCE(
|
||||
total_tokens,
|
||||
COALESCE(input_tokens, 0) + COALESCE(output_tokens, 0)
|
||||
)
|
||||
GREATEST(COALESCE(total_tokens, 0), 0)
|
||||
),
|
||||
0
|
||||
) AS total_tokens
|
||||
)::BIGINT AS total_tokens
|
||||
FROM usage_billing_facts AS "usage"
|
||||
WHERE api_key_id = ANY($1::TEXT[])
|
||||
GROUP BY api_key_id
|
||||
|
||||
@@ -7,11 +7,11 @@ use super::{
|
||||
inflate_usage_json_value, prepare_request_metadata_for_body_storage,
|
||||
prepare_usage_body_storage, resolved_read_usage_body_ref, resolved_write_usage_body_ref,
|
||||
split_dashboard_daily_aggregate_range, split_dashboard_hourly_aggregate_range,
|
||||
usage_body_capture_state_for_storage, usage_body_ref, usage_http_audit_body_refs,
|
||||
usage_http_audit_capture_mode, usage_routing_snapshot_from_usage,
|
||||
usage_settlement_pricing_snapshot_from_usage, AggregateRangeSplit, SqlxUsageReadRepository,
|
||||
UsageHttpAuditRefs, UsageRoutingSnapshot, UsageSettlementPricingSnapshot,
|
||||
MAX_INLINE_USAGE_BODY_BYTES,
|
||||
usage_body_capture_state_for_storage, usage_body_ref, usage_effective_input_tokens,
|
||||
usage_http_audit_body_refs, usage_http_audit_capture_mode, usage_routing_snapshot_from_usage,
|
||||
usage_settlement_pricing_snapshot_from_usage, usage_total_input_context, AggregateRangeSplit,
|
||||
SqlxUsageReadRepository, UsageHttpAuditRefs, UsageRoutingSnapshot,
|
||||
UsageSettlementPricingSnapshot, MAX_INLINE_USAGE_BODY_BYTES,
|
||||
};
|
||||
use crate::{PostgresPoolConfig, PostgresPoolFactory};
|
||||
use aether_data_contracts::repository::usage::{
|
||||
@@ -600,8 +600,25 @@ fn usage_sql_does_not_require_updated_at_column() {
|
||||
|
||||
#[test]
|
||||
fn usage_sql_summarizes_tokens_by_api_key_ids_in_database() {
|
||||
assert!(super::SUMMARIZE_TOTAL_TOKENS_BY_API_KEY_IDS_SQL.contains("GROUP BY api_key_id"));
|
||||
assert!(super::SUMMARIZE_TOTAL_TOKENS_BY_API_KEY_IDS_SQL.contains("ANY($1::TEXT[])"));
|
||||
let sql = super::SUMMARIZE_TOTAL_TOKENS_BY_API_KEY_IDS_SQL;
|
||||
assert!(sql.contains("GROUP BY api_key_id"));
|
||||
assert!(sql.contains("ANY($1::TEXT[])"));
|
||||
assert!(sql.contains("GREATEST(COALESCE(total_tokens, 0), 0)"));
|
||||
assert!(sql.contains(")::BIGINT AS total_tokens"));
|
||||
assert!(!sql.contains("COALESCE(input_tokens, 0) + COALESCE(output_tokens, 0)"));
|
||||
|
||||
let source = include_str!("mod.rs");
|
||||
let implementation = source
|
||||
.split("pub async fn summarize_total_tokens_by_api_key_ids")
|
||||
.nth(1)
|
||||
.and_then(|tail| {
|
||||
tail.split("pub async fn summarize_usage_totals_by_user_ids")
|
||||
.next()
|
||||
})
|
||||
.expect("API-key total-token summary implementation should be present");
|
||||
assert!(implementation.contains("SUM(GREATEST(COALESCE(total_tokens, 0), 0))"));
|
||||
assert!(implementation.contains(")::BIGINT AS total_tokens"));
|
||||
assert!(!implementation.contains("COALESCE(input_tokens, 0) + COALESCE(output_tokens, 0)"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -674,31 +691,24 @@ fn usage_counter_pending_health_does_not_scan_processed_history() {
|
||||
|
||||
#[test]
|
||||
fn usage_sql_rebuild_matches_online_api_key_usage_semantics() {
|
||||
assert!(super::REBUILD_API_KEY_USAGE_STATS_SQL.contains("COUNT(*)::BIGINT"));
|
||||
assert!(super::REBUILD_API_KEY_USAGE_STATS_SQL.contains("COALESCE("));
|
||||
assert!(super::REBUILD_API_KEY_USAGE_STATS_SQL.contains("total_tokens,"));
|
||||
assert!(super::REBUILD_API_KEY_USAGE_STATS_SQL
|
||||
.contains("COALESCE(input_tokens, 0) + COALESCE(output_tokens, 0)"));
|
||||
assert!(super::REBUILD_API_KEY_USAGE_STATS_SQL.contains("AND BTRIM(api_key_id) <> ''"));
|
||||
assert!(super::REBUILD_API_KEY_USAGE_STATS_SQL
|
||||
.contains("AND status NOT IN ('pending', 'streaming')"));
|
||||
let sql = super::REBUILD_API_KEY_USAGE_STATS_SQL;
|
||||
assert!(sql.contains("COUNT(*)::BIGINT"));
|
||||
assert!(sql.contains("GREATEST(\n COALESCE(total_tokens, 0),"));
|
||||
assert!(!sql.contains("COALESCE(input_tokens, 0) + COALESCE(output_tokens, 0)"));
|
||||
assert!(sql.contains("AND BTRIM(api_key_id) <> ''"));
|
||||
assert!(sql.contains("AND status NOT IN ('pending', 'streaming')"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn usage_sql_rebuild_matches_online_provider_key_usage_semantics() {
|
||||
assert!(super::REBUILD_PROVIDER_API_KEY_USAGE_STATS_SQL.contains("COUNT(*)::BIGINT"));
|
||||
assert!(super::REBUILD_PROVIDER_API_KEY_USAGE_STATS_SQL
|
||||
.contains("NULLIF(BTRIM(error_message), '') IS NULL"));
|
||||
assert!(super::REBUILD_PROVIDER_API_KEY_USAGE_STATS_SQL.contains("COALESCE("));
|
||||
assert!(super::REBUILD_PROVIDER_API_KEY_USAGE_STATS_SQL.contains("total_tokens,"));
|
||||
assert!(super::REBUILD_PROVIDER_API_KEY_USAGE_STATS_SQL
|
||||
.contains("COALESCE(input_tokens, 0) + COALESCE(output_tokens, 0)"));
|
||||
assert!(super::REBUILD_PROVIDER_API_KEY_USAGE_STATS_SQL
|
||||
.contains("AND BTRIM(provider_api_key_id) <> ''"));
|
||||
assert!(super::REBUILD_PROVIDER_API_KEY_USAGE_STATS_SQL
|
||||
.contains("WHEN status NOT IN ('pending', 'streaming')"));
|
||||
assert!(super::REBUILD_PROVIDER_API_KEY_USAGE_STATS_SQL
|
||||
.contains("WHEN status IN ('pending', 'streaming') THEN 0"));
|
||||
let sql = super::REBUILD_PROVIDER_API_KEY_USAGE_STATS_SQL;
|
||||
assert!(sql.contains("COUNT(*)::BIGINT"));
|
||||
assert!(sql.contains("NULLIF(BTRIM(error_message), '') IS NULL"));
|
||||
assert!(sql.contains("GREATEST(\n COALESCE(total_tokens, 0),"));
|
||||
assert!(!sql.contains("COALESCE(input_tokens, 0) + COALESCE(output_tokens, 0)"));
|
||||
assert!(sql.contains("AND BTRIM(provider_api_key_id) <> ''"));
|
||||
assert!(sql.contains("WHEN status NOT IN ('pending', 'streaming')"));
|
||||
assert!(sql.contains("WHEN status IN ('pending', 'streaming') THEN 0"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -885,6 +895,82 @@ fn usage_sql_summarize_usage_leaderboard_supports_daily_aggregates() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn usage_sql_aggregate_reads_use_materialized_total_tokens_only_when_available() {
|
||||
fn select_projections_before_table<'a>(source: &'a str, table: &str) -> Vec<&'a str> {
|
||||
let marker = format!("FROM {table}\n");
|
||||
let mut remaining = source;
|
||||
let mut projections = Vec::new();
|
||||
while let Some((before_table, after_table)) = remaining.split_once(marker.as_str()) {
|
||||
projections.push(
|
||||
before_table
|
||||
.rsplit_once("SELECT")
|
||||
.map(|(_, projection)| projection)
|
||||
.unwrap_or_else(|| panic!("SELECT projection for {table} should be present")),
|
||||
);
|
||||
remaining = after_table;
|
||||
}
|
||||
projections
|
||||
}
|
||||
|
||||
let source = include_str!("mod.rs");
|
||||
let breakdown = source
|
||||
.split("async fn summarize_usage_breakdown_from_daily_aggregates")
|
||||
.nth(1)
|
||||
.and_then(|tail| tail.split("async fn summarize_usage_breakdown_raw").next())
|
||||
.expect("daily aggregate usage breakdown query should be present");
|
||||
for table in [
|
||||
"stats_user_daily_model",
|
||||
"stats_user_daily_provider",
|
||||
"stats_user_daily_api_format",
|
||||
] {
|
||||
assert!(breakdown.contains(table));
|
||||
}
|
||||
assert!(breakdown.contains("COALESCE(SUM(total_tokens), 0)::BIGINT AS total_tokens"));
|
||||
assert!(!breakdown.contains(
|
||||
"SUM(effective_input_tokens + output_tokens + cache_creation_tokens + cache_read_tokens)"
|
||||
));
|
||||
|
||||
let leaderboard = source
|
||||
.split("async fn summarize_usage_leaderboard_from_daily_aggregates")
|
||||
.nth(1)
|
||||
.and_then(|tail| {
|
||||
tail.split("pub async fn summarize_usage_leaderboard")
|
||||
.next()
|
||||
})
|
||||
.expect("daily aggregate usage leaderboard queries should be present");
|
||||
for (table, expected_queries) in [
|
||||
("stats_user_daily_provider", 1),
|
||||
("stats_user_daily_model", 2),
|
||||
] {
|
||||
let projections = select_projections_before_table(leaderboard, table);
|
||||
assert_eq!(
|
||||
projections.len(),
|
||||
expected_queries,
|
||||
"unexpected {table} query count"
|
||||
);
|
||||
assert!(projections.iter().all(|projection| projection
|
||||
.contains("COALESCE(SUM(total_tokens), 0)::BIGINT AS total_tokens")));
|
||||
}
|
||||
|
||||
for (table, component_expression) in [
|
||||
(
|
||||
"stats_user_daily",
|
||||
"SUM(effective_input_tokens + output_tokens + cache_creation_tokens + cache_read_tokens)",
|
||||
),
|
||||
(
|
||||
"stats_daily_model",
|
||||
"SUM(input_tokens + output_tokens + cache_creation_tokens + cache_read_tokens)",
|
||||
),
|
||||
("stats_daily_api_key", "stats_daily_api_key.input_tokens"),
|
||||
] {
|
||||
let projections = select_projections_before_table(leaderboard, table);
|
||||
assert_eq!(projections.len(), 1, "unexpected {table} query count");
|
||||
assert!(projections[0].contains(component_expression));
|
||||
assert!(!projections[0].contains("SUM(total_tokens)"));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn usage_sql_aggregate_usage_audits_supports_daily_model_and_provider_aggregates() {
|
||||
let source = include_str!("mod.rs");
|
||||
@@ -991,6 +1077,189 @@ fn usage_sql_raw_aggregates_use_canonical_billing_facts() {
|
||||
assert!(!source.contains("apply_provider_api_key_codex_window_usage_delta_in_tx"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn usage_sql_raw_token_aggregates_do_not_renormalize_canonical_billing_facts() {
|
||||
let source = include_str!("mod.rs");
|
||||
for (start, end, label) in [
|
||||
(
|
||||
"async fn summarize_usage_audits_raw",
|
||||
"pub async fn summarize_usage_audits",
|
||||
"usage audit summary",
|
||||
),
|
||||
(
|
||||
"async fn summarize_usage_cache_affinity_hit_summary_raw",
|
||||
"async fn summarize_usage_cache_affinity_hit_summary_from_daily_aggregates",
|
||||
"cache affinity",
|
||||
),
|
||||
(
|
||||
"async fn summarize_usage_breakdown_raw",
|
||||
"pub async fn summarize_usage_breakdown",
|
||||
"usage breakdown",
|
||||
),
|
||||
(
|
||||
"async fn summarize_usage_leaderboard_raw",
|
||||
"async fn summarize_usage_leaderboard_from_daily_aggregates",
|
||||
"usage leaderboard",
|
||||
),
|
||||
(
|
||||
"async fn aggregate_usage_audits_raw",
|
||||
"pub async fn aggregate_usage_audits",
|
||||
"usage audit aggregation",
|
||||
),
|
||||
(
|
||||
"async fn summarize_usage_daily_heatmap_raw_from_range",
|
||||
"async fn summarize_usage_daily_heatmap_from_daily_aggregates",
|
||||
"usage daily heatmap",
|
||||
),
|
||||
] {
|
||||
let body = source
|
||||
.split(start)
|
||||
.nth(1)
|
||||
.and_then(|tail| tail.split(end).next())
|
||||
.unwrap_or_else(|| panic!("{label} raw query should be present"));
|
||||
assert!(body.contains("FROM usage_billing_facts"));
|
||||
assert!(
|
||||
!body.contains("split_part(lower("),
|
||||
"{label} must not normalize canonical billing facts by API format"
|
||||
);
|
||||
assert!(
|
||||
!body.contains("input_tokens - cache_"),
|
||||
"{label} must not subtract cache tokens from canonical input tokens"
|
||||
);
|
||||
}
|
||||
|
||||
let cache_affinity = source
|
||||
.split("async fn summarize_usage_cache_affinity_hit_summary_raw")
|
||||
.nth(1)
|
||||
.and_then(|tail| {
|
||||
tail.split("async fn summarize_usage_cache_affinity_hit_summary_from_daily_aggregates")
|
||||
.next()
|
||||
})
|
||||
.expect("cache affinity raw query should be present");
|
||||
assert!(cache_affinity.contains("\"usage\".input_tokens"));
|
||||
assert!(!cache_affinity.contains("\"usage\".effective_input_tokens"));
|
||||
assert!(cache_affinity.contains("\"usage\".total_input_context"));
|
||||
|
||||
let audit_summary = source
|
||||
.split("async fn summarize_usage_audits_raw")
|
||||
.nth(1)
|
||||
.and_then(|tail| tail.split("pub async fn summarize_usage_audits").next())
|
||||
.expect("usage audit summary raw query should be present");
|
||||
assert!(audit_summary.contains("SUM(GREATEST(COALESCE(\"usage\".cache_creation_input_tokens"));
|
||||
assert!(!audit_summary.contains("WHEN COALESCE(\"usage\".cache_creation_input_tokens, 0) = 0"));
|
||||
|
||||
for (start, end, label) in [
|
||||
(
|
||||
"async fn summarize_usage_breakdown_raw",
|
||||
"pub async fn summarize_usage_breakdown",
|
||||
"usage breakdown",
|
||||
),
|
||||
(
|
||||
"async fn aggregate_usage_audits_raw",
|
||||
"pub async fn aggregate_usage_audits",
|
||||
"usage audit aggregation",
|
||||
),
|
||||
] {
|
||||
let body = source
|
||||
.split(start)
|
||||
.nth(1)
|
||||
.and_then(|tail| tail.split(end).next())
|
||||
.unwrap_or_else(|| panic!("{label} raw query should be present"));
|
||||
assert!(body.contains("\"usage\".effective_input_tokens"));
|
||||
assert!(body.contains("\"usage\".total_input_context"));
|
||||
}
|
||||
|
||||
let leaderboard = source
|
||||
.split("async fn summarize_usage_leaderboard_raw")
|
||||
.nth(1)
|
||||
.and_then(|tail| {
|
||||
tail.split("async fn summarize_usage_leaderboard_from_daily_aggregates")
|
||||
.next()
|
||||
})
|
||||
.expect("usage leaderboard raw query should be present");
|
||||
assert!(leaderboard.contains("SUM(GREATEST(COALESCE(\"usage\".total_tokens"));
|
||||
|
||||
let daily_heatmap = source
|
||||
.split("async fn summarize_usage_daily_heatmap_raw_from_range")
|
||||
.nth(1)
|
||||
.and_then(|tail| {
|
||||
tail.split("async fn summarize_usage_daily_heatmap_from_daily_aggregates")
|
||||
.next()
|
||||
})
|
||||
.expect("usage daily heatmap raw query should be present");
|
||||
assert!(daily_heatmap.contains("SUM(GREATEST(COALESCE(\"usage\".total_tokens"));
|
||||
assert!(!daily_heatmap.contains("\"usage\".input_tokens + \"usage\".output_tokens"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn usage_sql_canonical_openai_cache_case_preserves_effective_and_total_tokens() {
|
||||
let upstream_input_tokens = 166_103_i64;
|
||||
let effective_input_tokens = 1_495_i64;
|
||||
let cache_creation_tokens = 0_i64;
|
||||
let cache_read_tokens = 164_608_i64;
|
||||
let output_tokens = 94_i64;
|
||||
let total_input_context = 166_103_i64;
|
||||
let total_tokens = 166_197_i64;
|
||||
|
||||
assert_eq!(
|
||||
effective_input_tokens + cache_creation_tokens + cache_read_tokens,
|
||||
total_input_context
|
||||
);
|
||||
assert_eq!(total_input_context, upstream_input_tokens);
|
||||
assert_eq!(
|
||||
effective_input_tokens + cache_creation_tokens + cache_read_tokens + output_tokens,
|
||||
total_tokens
|
||||
);
|
||||
|
||||
let source = include_str!("mod.rs");
|
||||
assert!(source.contains("\"usage\".effective_input_tokens"));
|
||||
assert!(source.contains("\"usage\".total_input_context"));
|
||||
assert!(!source.contains("split_part(lower("));
|
||||
|
||||
let aggregate_audit_summary = source
|
||||
.split("async fn summarize_usage_audits_from_daily_aggregates")
|
||||
.nth(1)
|
||||
.and_then(|tail| tail.split("async fn summarize_usage_audits_raw").next())
|
||||
.expect("daily aggregate usage audit summary should be present");
|
||||
assert_eq!(
|
||||
aggregate_audit_summary
|
||||
.matches("WHEN effective_input_tokens = 0 AND total_input_context = 0")
|
||||
.count(),
|
||||
2
|
||||
);
|
||||
assert_eq!(
|
||||
aggregate_audit_summary
|
||||
.matches("+ output_tokens + cache_creation_tokens + cache_read_tokens")
|
||||
.count(),
|
||||
2
|
||||
);
|
||||
assert!(!aggregate_audit_summary.contains("SUM(input_tokens + output_tokens)"));
|
||||
|
||||
let raw_audit_summary = source
|
||||
.split("async fn summarize_usage_audits_raw")
|
||||
.nth(1)
|
||||
.and_then(|tail| tail.split("pub async fn summarize_usage_audits").next())
|
||||
.expect("raw usage audit summary should be present");
|
||||
assert!(raw_audit_summary.contains("SUM(GREATEST(COALESCE(\"usage\".total_tokens, 0), 0))"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn usage_openai_total_input_context_includes_cache_creation_tokens() {
|
||||
let effective_input_tokens =
|
||||
usage_effective_input_tokens(Some(1_000), Some(100), Some(400), "openai");
|
||||
assert_eq!(effective_input_tokens, Some(500));
|
||||
assert_eq!(
|
||||
usage_total_input_context(
|
||||
Some(1_000),
|
||||
effective_input_tokens,
|
||||
Some(100),
|
||||
Some(400),
|
||||
"openai",
|
||||
),
|
||||
Some(1_000)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dashboard_covering_index_keeps_canonical_billing_reads_heap_light() {
|
||||
let migration = include_str!(
|
||||
@@ -1134,6 +1403,22 @@ fn usage_billing_facts_projects_upstream_stream_mode() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn usage_billing_facts_total_tokens_uses_canonical_effective_input() {
|
||||
let migration =
|
||||
include_str!("../../migrations/20260716000000_fix_usage_billing_facts_total_tokens.sql");
|
||||
let bootstrap =
|
||||
include_str!("../../../../runtime/schema/bootstrap/postgres/100_usage_capture.sql");
|
||||
|
||||
for sql in [migration, bootstrap] {
|
||||
assert!(sql.contains("WHEN settlement.billing_effective_input_tokens IS NOT NULL"));
|
||||
assert!(sql.contains("GREATEST(settlement.billing_effective_input_tokens, 0)"));
|
||||
assert!(sql.contains("WHEN settlement.billing_total_input_context IS NOT NULL"));
|
||||
assert!(sql.contains("NULLIF(GREATEST(COALESCE(usage_rows.total_tokens, 0), 0), 0)"));
|
||||
assert!(!sql.contains("THEN COALESCE(settlement.billing_input_tokens, 0)"));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn usage_sql_reads_http_audits_for_single_record_fetches() {
|
||||
assert!(super::FIND_BY_REQUEST_ID_SQL.contains("LEFT JOIN usage_http_audits"));
|
||||
@@ -1157,11 +1442,24 @@ fn usage_sql_reads_settlement_snapshots_for_single_record_fetches() {
|
||||
assert!(super::FIND_BY_ID_SQL.contains("LEFT JOIN usage_settlement_snapshots"));
|
||||
assert!(super::FIND_BY_REQUEST_ID_SQL.contains("settlement_billing_snapshot_schema_version"));
|
||||
assert!(super::FIND_BY_ID_SQL.contains("settlement_price_per_request"));
|
||||
for sql in [super::FIND_BY_REQUEST_ID_SQL, super::FIND_BY_ID_SQL] {
|
||||
for sql in [
|
||||
super::FIND_BY_REQUEST_ID_SQL,
|
||||
super::FIND_BY_ID_SQL,
|
||||
super::LIST_USAGE_AUDITS_PREFIX,
|
||||
super::LIST_RECENT_USAGE_AUDITS_PREFIX,
|
||||
] {
|
||||
assert!(sql.contains("CAST(\"usage\".input_tokens AS INTEGER) AS input_tokens"));
|
||||
assert!(sql.contains(
|
||||
"usage_settlement_snapshots.billing_input_tokens AS settlement_billing_input_tokens"
|
||||
));
|
||||
assert!(sql.contains(
|
||||
"WHEN usage_settlement_snapshots.billing_effective_input_tokens IS NOT NULL"
|
||||
));
|
||||
assert!(
|
||||
sql.contains("WHEN usage_settlement_snapshots.billing_total_input_context IS NOT NULL")
|
||||
);
|
||||
assert!(sql.contains("NULLIF(GREATEST(COALESCE(\"usage\".total_tokens, 0), 0), 0)"));
|
||||
assert!(!sql.contains("THEN COALESCE(usage_settlement_snapshots.billing_input_tokens, 0)"));
|
||||
assert!(sql.contains("usage_settlement_snapshots.billing_cache_creation_5m_tokens"));
|
||||
assert!(sql.contains(
|
||||
"CAST(usage_settlement_snapshots.billing_total_cost_usd AS DOUBLE PRECISION)"
|
||||
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
CREATE TEMP TABLE tmp_canonical_usage_token_totals ON COMMIT DROP AS
|
||||
SELECT
|
||||
usage.user_id,
|
||||
usage.api_key_id,
|
||||
usage.provider_api_key_id,
|
||||
usage.provider_name,
|
||||
usage.model,
|
||||
usage.api_format,
|
||||
(date_trunc('day', usage.created_at AT TIME ZONE 'UTC') AT TIME ZONE 'UTC') AS day_utc,
|
||||
GREATEST(COALESCE(usage.total_tokens, 0), 0)::BIGINT AS total_tokens
|
||||
FROM public.usage_billing_facts AS usage
|
||||
WHERE usage.status NOT IN ('pending', 'streaming');
|
||||
|
||||
CREATE INDEX tmp_canonical_usage_token_totals_day_idx
|
||||
ON tmp_canonical_usage_token_totals (day_utc);
|
||||
CREATE INDEX tmp_canonical_usage_token_totals_api_key_idx
|
||||
ON tmp_canonical_usage_token_totals (api_key_id);
|
||||
CREATE INDEX tmp_canonical_usage_token_totals_provider_key_idx
|
||||
ON tmp_canonical_usage_token_totals (provider_api_key_id);
|
||||
|
||||
ANALYZE tmp_canonical_usage_token_totals;
|
||||
|
||||
WITH aggregated AS (
|
||||
SELECT
|
||||
day_utc,
|
||||
model,
|
||||
provider_name,
|
||||
COALESCE(SUM(total_tokens), 0)::BIGINT AS total_tokens
|
||||
FROM tmp_canonical_usage_token_totals
|
||||
WHERE model IS NOT NULL
|
||||
AND model <> ''
|
||||
AND provider_name IS NOT NULL
|
||||
AND provider_name <> ''
|
||||
AND provider_name NOT IN ('unknown', 'pending')
|
||||
GROUP BY day_utc, model, provider_name
|
||||
)
|
||||
UPDATE public.stats_daily_model_provider AS target
|
||||
SET
|
||||
total_tokens = aggregated.total_tokens,
|
||||
updated_at = NOW()
|
||||
FROM aggregated
|
||||
WHERE target.date = aggregated.day_utc
|
||||
AND target.model = aggregated.model
|
||||
AND target.provider_name = aggregated.provider_name;
|
||||
|
||||
WITH aggregated AS (
|
||||
SELECT
|
||||
user_id,
|
||||
day_utc,
|
||||
model,
|
||||
COALESCE(SUM(total_tokens), 0)::BIGINT AS total_tokens
|
||||
FROM tmp_canonical_usage_token_totals
|
||||
WHERE user_id IS NOT NULL
|
||||
AND model IS NOT NULL
|
||||
AND model <> ''
|
||||
AND provider_name NOT IN ('unknown', 'pending')
|
||||
GROUP BY user_id, day_utc, model
|
||||
)
|
||||
UPDATE public.stats_user_daily_model AS target
|
||||
SET
|
||||
total_tokens = aggregated.total_tokens,
|
||||
updated_at = NOW()
|
||||
FROM aggregated
|
||||
WHERE target.user_id = aggregated.user_id
|
||||
AND target.date = aggregated.day_utc
|
||||
AND target.model = aggregated.model;
|
||||
|
||||
WITH aggregated AS (
|
||||
SELECT
|
||||
user_id,
|
||||
day_utc,
|
||||
provider_name,
|
||||
COALESCE(SUM(total_tokens), 0)::BIGINT AS total_tokens
|
||||
FROM tmp_canonical_usage_token_totals
|
||||
WHERE user_id IS NOT NULL
|
||||
AND provider_name IS NOT NULL
|
||||
AND provider_name <> ''
|
||||
AND provider_name NOT IN ('unknown', 'pending')
|
||||
GROUP BY user_id, day_utc, provider_name
|
||||
)
|
||||
UPDATE public.stats_user_daily_provider AS target
|
||||
SET
|
||||
total_tokens = aggregated.total_tokens,
|
||||
updated_at = NOW()
|
||||
FROM aggregated
|
||||
WHERE target.user_id = aggregated.user_id
|
||||
AND target.date = aggregated.day_utc
|
||||
AND target.provider_name = aggregated.provider_name;
|
||||
|
||||
WITH aggregated AS (
|
||||
SELECT
|
||||
user_id,
|
||||
day_utc,
|
||||
api_format,
|
||||
COALESCE(SUM(total_tokens), 0)::BIGINT AS total_tokens
|
||||
FROM tmp_canonical_usage_token_totals
|
||||
WHERE user_id IS NOT NULL
|
||||
AND api_format IS NOT NULL
|
||||
AND provider_name NOT IN ('unknown', 'pending')
|
||||
GROUP BY user_id, day_utc, api_format
|
||||
)
|
||||
UPDATE public.stats_user_daily_api_format AS target
|
||||
SET
|
||||
total_tokens = aggregated.total_tokens,
|
||||
updated_at = NOW()
|
||||
FROM aggregated
|
||||
WHERE target.user_id = aggregated.user_id
|
||||
AND target.date = aggregated.day_utc
|
||||
AND target.api_format = aggregated.api_format;
|
||||
|
||||
WITH aggregated AS (
|
||||
SELECT
|
||||
user_id,
|
||||
day_utc,
|
||||
model,
|
||||
provider_name,
|
||||
COALESCE(SUM(total_tokens), 0)::BIGINT AS total_tokens
|
||||
FROM tmp_canonical_usage_token_totals
|
||||
WHERE user_id IS NOT NULL
|
||||
AND model IS NOT NULL
|
||||
AND model <> ''
|
||||
AND provider_name IS NOT NULL
|
||||
AND provider_name <> ''
|
||||
AND provider_name NOT IN ('unknown', 'pending')
|
||||
GROUP BY user_id, day_utc, model, provider_name
|
||||
)
|
||||
UPDATE public.stats_user_daily_model_provider AS target
|
||||
SET
|
||||
total_tokens = aggregated.total_tokens,
|
||||
updated_at = NOW()
|
||||
FROM aggregated
|
||||
WHERE target.user_id = aggregated.user_id
|
||||
AND target.date = aggregated.day_utc
|
||||
AND target.model = aggregated.model
|
||||
AND target.provider_name = aggregated.provider_name;
|
||||
|
||||
UPDATE public.api_keys
|
||||
SET total_tokens = 0;
|
||||
|
||||
WITH aggregated AS (
|
||||
SELECT
|
||||
api_key_id,
|
||||
COALESCE(SUM(total_tokens), 0)::BIGINT AS total_tokens
|
||||
FROM tmp_canonical_usage_token_totals
|
||||
WHERE api_key_id IS NOT NULL
|
||||
AND BTRIM(api_key_id) <> ''
|
||||
GROUP BY api_key_id
|
||||
)
|
||||
UPDATE public.api_keys AS target
|
||||
SET total_tokens = aggregated.total_tokens
|
||||
FROM aggregated
|
||||
WHERE target.id = aggregated.api_key_id;
|
||||
|
||||
UPDATE public.provider_api_keys
|
||||
SET total_tokens = 0;
|
||||
|
||||
WITH aggregated AS (
|
||||
SELECT
|
||||
provider_api_key_id,
|
||||
COALESCE(SUM(total_tokens), 0)::BIGINT AS total_tokens
|
||||
FROM tmp_canonical_usage_token_totals
|
||||
WHERE provider_api_key_id IS NOT NULL
|
||||
AND BTRIM(provider_api_key_id) <> ''
|
||||
GROUP BY provider_api_key_id
|
||||
)
|
||||
UPDATE public.provider_api_keys AS target
|
||||
SET total_tokens = aggregated.total_tokens
|
||||
FROM aggregated
|
||||
WHERE target.id = aggregated.provider_api_key_id;
|
||||
@@ -212,10 +212,35 @@ SELECT
|
||||
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')
|
||||
= 'openai'
|
||||
AND (
|
||||
GREATEST(COALESCE(usage_rows.cache_creation_input_tokens, 0), 0) > 0
|
||||
OR GREATEST(COALESCE(usage_rows.cache_creation_input_tokens_5m, 0), 0) > 0
|
||||
OR GREATEST(COALESCE(usage_rows.cache_creation_input_tokens_1h, 0), 0) > 0
|
||||
OR GREATEST(COALESCE(usage_rows.cache_read_input_tokens, 0), 0) > 0
|
||||
)
|
||||
THEN GREATEST(
|
||||
GREATEST(COALESCE(usage_rows.input_tokens, 0), 0)
|
||||
- GREATEST(
|
||||
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
|
||||
)
|
||||
- GREATEST(COALESCE(usage_rows.cache_read_input_tokens, 0), 0),
|
||||
0
|
||||
)
|
||||
WHEN split_part(lower(COALESCE(COALESCE(usage_rows.endpoint_api_format, usage_rows.api_format), '')), ':', 1)
|
||||
IN ('gemini', 'google')
|
||||
AND GREATEST(COALESCE(usage_rows.cache_read_input_tokens, 0), 0) > 0
|
||||
THEN GREATEST(
|
||||
GREATEST(COALESCE(usage_rows.input_tokens, 0), 0)
|
||||
- GREATEST(COALESCE(usage_rows.cache_read_input_tokens, 0), 0),
|
||||
@@ -231,6 +256,12 @@ SELECT
|
||||
GREATEST(
|
||||
COALESCE(
|
||||
settlement.billing_cache_creation_tokens,
|
||||
CASE
|
||||
WHEN settlement.billing_cache_creation_5m_tokens IS NOT NULL
|
||||
OR settlement.billing_cache_creation_1h_tokens IS NOT NULL
|
||||
THEN COALESCE(settlement.billing_cache_creation_5m_tokens, 0)
|
||||
+ COALESCE(settlement.billing_cache_creation_1h_tokens, 0)
|
||||
END,
|
||||
CASE
|
||||
WHEN COALESCE(usage_rows.cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
@@ -266,29 +297,108 @@ SELECT
|
||||
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),
|
||||
WHEN settlement.billing_effective_input_tokens IS NOT NULL
|
||||
THEN GREATEST(settlement.billing_effective_input_tokens, 0)
|
||||
+ GREATEST(COALESCE(settlement.billing_output_tokens, usage_rows.output_tokens, 0), 0)
|
||||
+ GREATEST(
|
||||
COALESCE(
|
||||
settlement.billing_cache_creation_tokens,
|
||||
CASE
|
||||
WHEN settlement.billing_cache_creation_5m_tokens IS NOT NULL
|
||||
OR settlement.billing_cache_creation_1h_tokens IS NOT NULL
|
||||
THEN COALESCE(settlement.billing_cache_creation_5m_tokens, 0)
|
||||
+ COALESCE(settlement.billing_cache_creation_1h_tokens, 0)
|
||||
END,
|
||||
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
|
||||
)
|
||||
+ COALESCE(settlement.billing_cache_read_tokens, 0)
|
||||
+ GREATEST(
|
||||
COALESCE(
|
||||
settlement.billing_cache_read_tokens,
|
||||
usage_rows.cache_read_input_tokens,
|
||||
0
|
||||
),
|
||||
0
|
||||
)
|
||||
WHEN settlement.billing_total_input_context IS NOT NULL
|
||||
THEN GREATEST(settlement.billing_total_input_context, 0)
|
||||
+ GREATEST(COALESCE(settlement.billing_output_tokens, usage_rows.output_tokens, 0), 0)
|
||||
END,
|
||||
NULLIF(GREATEST(COALESCE(usage_rows.total_tokens, 0), 0), 0),
|
||||
CASE
|
||||
WHEN split_part(lower(COALESCE(COALESCE(usage_rows.endpoint_api_format, usage_rows.api_format), '')), ':', 1)
|
||||
IN ('openai', 'gemini', 'google')
|
||||
THEN GREATEST(COALESCE(usage_rows.input_tokens, 0), 0)
|
||||
+ GREATEST(COALESCE(usage_rows.output_tokens, 0), 0)
|
||||
ELSE GREATEST(COALESCE(usage_rows.input_tokens, 0), 0)
|
||||
+ GREATEST(COALESCE(usage_rows.output_tokens, 0), 0)
|
||||
+ GREATEST(
|
||||
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
|
||||
)
|
||||
+ GREATEST(COALESCE(usage_rows.cache_read_input_tokens, 0), 0)
|
||||
END,
|
||||
usage_rows.total_tokens,
|
||||
0
|
||||
),
|
||||
0
|
||||
)::bigint AS total_tokens,
|
||||
GREATEST(
|
||||
COALESCE(
|
||||
CASE
|
||||
WHEN settlement.billing_effective_input_tokens IS NOT NULL
|
||||
THEN GREATEST(settlement.billing_effective_input_tokens, 0)
|
||||
+ GREATEST(
|
||||
COALESCE(
|
||||
settlement.billing_cache_creation_tokens,
|
||||
CASE
|
||||
WHEN settlement.billing_cache_creation_5m_tokens IS NOT NULL
|
||||
OR settlement.billing_cache_creation_1h_tokens IS NOT NULL
|
||||
THEN COALESCE(settlement.billing_cache_creation_5m_tokens, 0)
|
||||
+ COALESCE(settlement.billing_cache_creation_1h_tokens, 0)
|
||||
END,
|
||||
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
|
||||
)
|
||||
+ GREATEST(
|
||||
COALESCE(
|
||||
settlement.billing_cache_read_tokens,
|
||||
usage_rows.cache_read_input_tokens,
|
||||
0
|
||||
),
|
||||
0
|
||||
)
|
||||
END,
|
||||
settlement.billing_total_input_context,
|
||||
CASE
|
||||
WHEN split_part(lower(COALESCE(COALESCE(usage_rows.endpoint_api_format, usage_rows.api_format), '')), ':', 1)
|
||||
@@ -306,7 +416,49 @@ SELECT
|
||||
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')
|
||||
= 'openai'
|
||||
THEN CASE
|
||||
WHEN GREATEST(COALESCE(usage_rows.input_tokens, 0), 0) <= 0 THEN 0
|
||||
WHEN GREATEST(COALESCE(usage_rows.cache_creation_input_tokens, 0), 0) <= 0
|
||||
AND GREATEST(COALESCE(usage_rows.cache_creation_input_tokens_5m, 0), 0) <= 0
|
||||
AND GREATEST(COALESCE(usage_rows.cache_creation_input_tokens_1h, 0), 0) <= 0
|
||||
AND 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(
|
||||
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
|
||||
)
|
||||
- GREATEST(COALESCE(usage_rows.cache_read_input_tokens, 0), 0),
|
||||
0
|
||||
)
|
||||
END
|
||||
+ GREATEST(
|
||||
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
|
||||
)
|
||||
+ 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 ('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
|
||||
|
||||
@@ -24,18 +24,7 @@ SELECT
|
||||
) AS completed_input_tokens,
|
||||
CAST(
|
||||
COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN COALESCE(cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE(cache_creation_input_tokens, 0)
|
||||
END
|
||||
) FILTER (WHERE status = 'completed'),
|
||||
SUM(cache_creation_input_tokens) FILTER (WHERE status = 'completed'),
|
||||
0
|
||||
) AS BIGINT
|
||||
) AS completed_cache_creation_tokens,
|
||||
@@ -49,74 +38,7 @@ SELECT
|
||||
) AS completed_cache_read_tokens,
|
||||
CAST(
|
||||
COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN split_part(
|
||||
lower(COALESCE(COALESCE(endpoint_api_format, api_format), '')),
|
||||
':',
|
||||
1
|
||||
) IN ('claude', 'anthropic')
|
||||
THEN GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
+ CASE
|
||||
WHEN COALESCE(cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE(cache_creation_input_tokens, 0)
|
||||
END
|
||||
+ GREATEST(COALESCE(cache_read_input_tokens, 0), 0)
|
||||
WHEN split_part(
|
||||
lower(COALESCE(COALESCE(endpoint_api_format, api_format), '')),
|
||||
':',
|
||||
1
|
||||
) IN ('openai', 'gemini', 'google')
|
||||
THEN (
|
||||
CASE
|
||||
WHEN GREATEST(COALESCE(input_tokens, 0), 0) <= 0 THEN 0
|
||||
WHEN GREATEST(COALESCE(cache_read_input_tokens, 0), 0) <= 0
|
||||
THEN GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
ELSE GREATEST(
|
||||
GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
- GREATEST(COALESCE(cache_read_input_tokens, 0), 0),
|
||||
0
|
||||
)
|
||||
END
|
||||
) + GREATEST(COALESCE(cache_read_input_tokens, 0), 0)
|
||||
ELSE CASE
|
||||
WHEN (
|
||||
CASE
|
||||
WHEN COALESCE(cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE(cache_creation_input_tokens, 0)
|
||||
END
|
||||
) > 0
|
||||
THEN GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
+ (
|
||||
CASE
|
||||
WHEN COALESCE(cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE(cache_creation_input_tokens, 0)
|
||||
END
|
||||
)
|
||||
+ GREATEST(COALESCE(cache_read_input_tokens, 0), 0)
|
||||
ELSE GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
+ GREATEST(COALESCE(cache_read_input_tokens, 0), 0)
|
||||
END
|
||||
END
|
||||
) FILTER (WHERE status = 'completed'),
|
||||
SUM(total_input_context) FILTER (WHERE status = 'completed'),
|
||||
0
|
||||
) AS BIGINT
|
||||
) AS completed_total_input_context,
|
||||
@@ -203,21 +125,7 @@ SELECT
|
||||
) AS input_tokens,
|
||||
CAST(
|
||||
COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN GREATEST(COALESCE(input_tokens, 0), 0) <= 0 THEN 0
|
||||
WHEN GREATEST(COALESCE(cache_read_input_tokens, 0), 0) <= 0
|
||||
THEN GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
WHEN split_part(lower(COALESCE(COALESCE(endpoint_api_format, api_format), '')), ':', 1)
|
||||
IN ('openai', 'gemini', 'google')
|
||||
THEN GREATEST(
|
||||
GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
- GREATEST(COALESCE(cache_read_input_tokens, 0), 0),
|
||||
0
|
||||
)
|
||||
ELSE GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
END
|
||||
) FILTER (WHERE status NOT IN ('pending', 'streaming') AND provider_name NOT IN ('unknown', 'pending')),
|
||||
SUM(effective_input_tokens) FILTER (WHERE status NOT IN ('pending', 'streaming') AND provider_name NOT IN ('unknown', 'pending')),
|
||||
0
|
||||
) AS BIGINT
|
||||
) AS effective_input_tokens,
|
||||
@@ -226,18 +134,7 @@ SELECT
|
||||
) AS output_tokens,
|
||||
CAST(
|
||||
COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN COALESCE(cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE(cache_creation_input_tokens, 0)
|
||||
END
|
||||
) FILTER (WHERE status NOT IN ('pending', 'streaming') AND provider_name NOT IN ('unknown', 'pending')),
|
||||
SUM(cache_creation_input_tokens) FILTER (WHERE status NOT IN ('pending', 'streaming') AND provider_name NOT IN ('unknown', 'pending')),
|
||||
0
|
||||
) AS BIGINT
|
||||
) AS cache_creation_tokens,
|
||||
@@ -263,68 +160,7 @@ SELECT
|
||||
) AS cache_read_tokens,
|
||||
CAST(
|
||||
COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN split_part(lower(COALESCE(COALESCE(endpoint_api_format, api_format), '')), ':', 1)
|
||||
IN ('claude', 'anthropic')
|
||||
THEN GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
+ CASE
|
||||
WHEN COALESCE(cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE(cache_creation_input_tokens, 0)
|
||||
END
|
||||
+ GREATEST(COALESCE(cache_read_input_tokens, 0), 0)
|
||||
WHEN split_part(lower(COALESCE(COALESCE(endpoint_api_format, api_format), '')), ':', 1)
|
||||
IN ('openai', 'gemini', 'google')
|
||||
THEN (
|
||||
CASE
|
||||
WHEN GREATEST(COALESCE(input_tokens, 0), 0) <= 0 THEN 0
|
||||
WHEN GREATEST(COALESCE(cache_read_input_tokens, 0), 0) <= 0
|
||||
THEN GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
ELSE GREATEST(
|
||||
GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
- GREATEST(COALESCE(cache_read_input_tokens, 0), 0),
|
||||
0
|
||||
)
|
||||
END
|
||||
) + GREATEST(COALESCE(cache_read_input_tokens, 0), 0)
|
||||
ELSE CASE
|
||||
WHEN (
|
||||
CASE
|
||||
WHEN COALESCE(cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE(cache_creation_input_tokens, 0)
|
||||
END
|
||||
) > 0
|
||||
THEN GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
+ (
|
||||
CASE
|
||||
WHEN COALESCE(cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE(cache_creation_input_tokens, 0)
|
||||
END
|
||||
)
|
||||
+ GREATEST(COALESCE(cache_read_input_tokens, 0), 0)
|
||||
ELSE GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
+ GREATEST(COALESCE(cache_read_input_tokens, 0), 0)
|
||||
END
|
||||
END
|
||||
) FILTER (WHERE status NOT IN ('pending', 'streaming') AND provider_name NOT IN ('unknown', 'pending')),
|
||||
SUM(total_input_context) FILTER (WHERE status NOT IN ('pending', 'streaming') AND provider_name NOT IN ('unknown', 'pending')),
|
||||
0
|
||||
) AS BIGINT
|
||||
) AS total_input_context,
|
||||
@@ -550,23 +386,7 @@ WITH aggregated AS (
|
||||
CAST(COUNT(id) AS BIGINT) AS total_requests,
|
||||
CAST(COALESCE(SUM(input_tokens), 0) AS BIGINT) AS input_tokens,
|
||||
CAST(COALESCE(SUM(output_tokens), 0) AS BIGINT) AS output_tokens,
|
||||
CAST(
|
||||
COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN COALESCE(cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE(cache_creation_input_tokens, 0)
|
||||
END
|
||||
),
|
||||
0
|
||||
) AS BIGINT
|
||||
) AS cache_creation_tokens,
|
||||
CAST(COALESCE(SUM(cache_creation_input_tokens), 0) AS BIGINT) AS cache_creation_tokens,
|
||||
CAST(COALESCE(SUM(cache_creation_input_tokens_5m), 0) AS BIGINT)
|
||||
AS cache_creation_ephemeral_5m_tokens,
|
||||
CAST(COALESCE(SUM(cache_creation_input_tokens_1h), 0) AS BIGINT)
|
||||
@@ -1108,26 +928,7 @@ WITH aggregated AS (
|
||||
) AS BIGINT
|
||||
) AS error_requests,
|
||||
CAST(COALESCE(SUM(input_tokens), 0) AS BIGINT) AS input_tokens,
|
||||
CAST(
|
||||
COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN GREATEST(COALESCE(input_tokens, 0), 0) <= 0 THEN 0
|
||||
WHEN GREATEST(COALESCE(cache_read_input_tokens, 0), 0) <= 0
|
||||
THEN GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
WHEN split_part(lower(COALESCE(COALESCE(endpoint_api_format, api_format), '')), ':', 1)
|
||||
IN ('openai', 'gemini', 'google')
|
||||
THEN GREATEST(
|
||||
GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
- GREATEST(COALESCE(cache_read_input_tokens, 0), 0),
|
||||
0
|
||||
)
|
||||
ELSE GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
END
|
||||
),
|
||||
0
|
||||
) AS BIGINT
|
||||
) AS effective_input_tokens,
|
||||
CAST(COALESCE(SUM(effective_input_tokens), 0) AS BIGINT) AS effective_input_tokens,
|
||||
CAST(COALESCE(SUM(output_tokens), 0) AS BIGINT) AS output_tokens,
|
||||
CAST(COALESCE(SUM(cache_creation_input_tokens), 0) AS BIGINT) AS cache_creation_tokens,
|
||||
CAST(COALESCE(SUM(cache_read_input_tokens), 0) AS BIGINT) AS cache_read_tokens,
|
||||
@@ -1252,116 +1053,15 @@ WITH aggregated AS (
|
||||
) AS BIGINT
|
||||
) AS error_requests,
|
||||
CAST(COALESCE(SUM(input_tokens), 0) AS BIGINT) AS input_tokens,
|
||||
CAST(
|
||||
COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN GREATEST(COALESCE(input_tokens, 0), 0) <= 0 THEN 0
|
||||
WHEN GREATEST(COALESCE(cache_read_input_tokens, 0), 0) <= 0
|
||||
THEN GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
WHEN split_part(lower(COALESCE(COALESCE(endpoint_api_format, api_format), '')), ':', 1)
|
||||
IN ('openai', 'gemini', 'google')
|
||||
THEN GREATEST(
|
||||
GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
- GREATEST(COALESCE(cache_read_input_tokens, 0), 0),
|
||||
0
|
||||
)
|
||||
ELSE GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
END
|
||||
),
|
||||
0
|
||||
) AS BIGINT
|
||||
) AS effective_input_tokens,
|
||||
CAST(COALESCE(SUM(effective_input_tokens), 0) AS BIGINT) AS effective_input_tokens,
|
||||
CAST(COALESCE(SUM(output_tokens), 0) AS BIGINT) AS output_tokens,
|
||||
CAST(
|
||||
COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN COALESCE(cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE(cache_creation_input_tokens, 0)
|
||||
END
|
||||
),
|
||||
0
|
||||
) AS BIGINT
|
||||
) AS cache_creation_tokens,
|
||||
CAST(COALESCE(SUM(cache_creation_input_tokens), 0) AS BIGINT) AS cache_creation_tokens,
|
||||
CAST(COALESCE(SUM(cache_creation_input_tokens_5m), 0) AS BIGINT)
|
||||
AS cache_creation_ephemeral_5m_tokens,
|
||||
CAST(COALESCE(SUM(cache_creation_input_tokens_1h), 0) AS BIGINT)
|
||||
AS cache_creation_ephemeral_1h_tokens,
|
||||
CAST(COALESCE(SUM(cache_read_input_tokens), 0) AS BIGINT) AS cache_read_tokens,
|
||||
CAST(
|
||||
COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN split_part(lower(COALESCE(COALESCE(endpoint_api_format, api_format), '')), ':', 1)
|
||||
IN ('claude', 'anthropic')
|
||||
THEN GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
+ CASE
|
||||
WHEN COALESCE(cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE(cache_creation_input_tokens, 0)
|
||||
END
|
||||
+ GREATEST(COALESCE(cache_read_input_tokens, 0), 0)
|
||||
WHEN split_part(lower(COALESCE(COALESCE(endpoint_api_format, api_format), '')), ':', 1)
|
||||
IN ('openai', 'gemini', 'google')
|
||||
THEN (
|
||||
CASE
|
||||
WHEN GREATEST(COALESCE(input_tokens, 0), 0) <= 0 THEN 0
|
||||
WHEN GREATEST(COALESCE(cache_read_input_tokens, 0), 0) <= 0
|
||||
THEN GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
ELSE GREATEST(
|
||||
GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
- GREATEST(COALESCE(cache_read_input_tokens, 0), 0),
|
||||
0
|
||||
)
|
||||
END
|
||||
) + GREATEST(COALESCE(cache_read_input_tokens, 0), 0)
|
||||
ELSE CASE
|
||||
WHEN (
|
||||
CASE
|
||||
WHEN COALESCE(cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE(cache_creation_input_tokens, 0)
|
||||
END
|
||||
) > 0
|
||||
THEN GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
+ (
|
||||
CASE
|
||||
WHEN COALESCE(cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE(cache_creation_input_tokens, 0)
|
||||
END
|
||||
)
|
||||
+ GREATEST(COALESCE(cache_read_input_tokens, 0), 0)
|
||||
ELSE GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
+ GREATEST(COALESCE(cache_read_input_tokens, 0), 0)
|
||||
END
|
||||
END
|
||||
),
|
||||
0
|
||||
) AS BIGINT
|
||||
) AS total_input_context,
|
||||
CAST(COALESCE(SUM(total_input_context), 0) AS BIGINT) AS total_input_context,
|
||||
CAST(COALESCE(SUM(total_cost_usd), 0) AS DOUBLE PRECISION) AS total_cost,
|
||||
CAST(COALESCE(SUM(cache_creation_cost_usd), 0) AS DOUBLE PRECISION) AS cache_creation_cost,
|
||||
CAST(COALESCE(SUM(cache_read_cost_usd), 0) AS DOUBLE PRECISION) AS cache_read_cost,
|
||||
@@ -1609,117 +1309,16 @@ WITH aggregated AS (
|
||||
) AS BIGINT
|
||||
) AS success_requests,
|
||||
CAST(COALESCE(SUM(input_tokens), 0) AS BIGINT) AS input_tokens,
|
||||
CAST(
|
||||
COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN GREATEST(COALESCE(input_tokens, 0), 0) <= 0 THEN 0
|
||||
WHEN GREATEST(COALESCE(cache_read_input_tokens, 0), 0) <= 0
|
||||
THEN GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
WHEN split_part(lower(COALESCE(COALESCE(endpoint_api_format, api_format), '')), ':', 1)
|
||||
IN ('openai', 'gemini', 'google')
|
||||
THEN GREATEST(
|
||||
GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
- GREATEST(COALESCE(cache_read_input_tokens, 0), 0),
|
||||
0
|
||||
)
|
||||
ELSE GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
END
|
||||
),
|
||||
0
|
||||
) AS BIGINT
|
||||
) AS effective_input_tokens,
|
||||
CAST(COALESCE(SUM(effective_input_tokens), 0) AS BIGINT) AS effective_input_tokens,
|
||||
CAST(COALESCE(SUM(output_tokens), 0) AS BIGINT) AS output_tokens,
|
||||
CAST(COALESCE(SUM(total_tokens), 0) AS BIGINT) AS total_tokens,
|
||||
CAST(
|
||||
COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN COALESCE(cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE(cache_creation_input_tokens, 0)
|
||||
END
|
||||
),
|
||||
0
|
||||
) AS BIGINT
|
||||
) AS cache_creation_tokens,
|
||||
CAST(COALESCE(SUM(cache_creation_input_tokens), 0) AS BIGINT) AS cache_creation_tokens,
|
||||
CAST(COALESCE(SUM(cache_creation_input_tokens_5m), 0) AS BIGINT)
|
||||
AS cache_creation_ephemeral_5m_tokens,
|
||||
CAST(COALESCE(SUM(cache_creation_input_tokens_1h), 0) AS BIGINT)
|
||||
AS cache_creation_ephemeral_1h_tokens,
|
||||
CAST(COALESCE(SUM(cache_read_input_tokens), 0) AS BIGINT) AS cache_read_tokens,
|
||||
CAST(
|
||||
COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN split_part(lower(COALESCE(COALESCE(endpoint_api_format, api_format), '')), ':', 1)
|
||||
IN ('claude', 'anthropic')
|
||||
THEN GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
+ CASE
|
||||
WHEN COALESCE(cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE(cache_creation_input_tokens, 0)
|
||||
END
|
||||
+ GREATEST(COALESCE(cache_read_input_tokens, 0), 0)
|
||||
WHEN split_part(lower(COALESCE(COALESCE(endpoint_api_format, api_format), '')), ':', 1)
|
||||
IN ('openai', 'gemini', 'google')
|
||||
THEN (
|
||||
CASE
|
||||
WHEN GREATEST(COALESCE(input_tokens, 0), 0) <= 0 THEN 0
|
||||
WHEN GREATEST(COALESCE(cache_read_input_tokens, 0), 0) <= 0
|
||||
THEN GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
ELSE GREATEST(
|
||||
GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
- GREATEST(COALESCE(cache_read_input_tokens, 0), 0),
|
||||
0
|
||||
)
|
||||
END
|
||||
) + GREATEST(COALESCE(cache_read_input_tokens, 0), 0)
|
||||
ELSE CASE
|
||||
WHEN (
|
||||
CASE
|
||||
WHEN COALESCE(cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE(cache_creation_input_tokens, 0)
|
||||
END
|
||||
) > 0
|
||||
THEN GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
+ (
|
||||
CASE
|
||||
WHEN COALESCE(cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE(cache_creation_input_tokens, 0)
|
||||
END
|
||||
)
|
||||
+ GREATEST(COALESCE(cache_read_input_tokens, 0), 0)
|
||||
ELSE GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
+ GREATEST(COALESCE(cache_read_input_tokens, 0), 0)
|
||||
END
|
||||
END
|
||||
),
|
||||
0
|
||||
) AS BIGINT
|
||||
) AS total_input_context,
|
||||
CAST(COALESCE(SUM(total_input_context), 0) AS BIGINT) AS total_input_context,
|
||||
CAST(COALESCE(SUM(total_cost_usd), 0) AS DOUBLE PRECISION) AS total_cost,
|
||||
CAST(COALESCE(SUM(actual_total_cost_usd), 0) AS DOUBLE PRECISION) AS actual_total_cost,
|
||||
COALESCE(
|
||||
@@ -1878,117 +1477,16 @@ WITH aggregated AS (
|
||||
) AS BIGINT
|
||||
) AS success_requests,
|
||||
CAST(COALESCE(SUM(input_tokens), 0) AS BIGINT) AS input_tokens,
|
||||
CAST(
|
||||
COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN GREATEST(COALESCE(input_tokens, 0), 0) <= 0 THEN 0
|
||||
WHEN GREATEST(COALESCE(cache_read_input_tokens, 0), 0) <= 0
|
||||
THEN GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
WHEN split_part(lower(COALESCE(COALESCE(endpoint_api_format, api_format), '')), ':', 1)
|
||||
IN ('openai', 'gemini', 'google')
|
||||
THEN GREATEST(
|
||||
GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
- GREATEST(COALESCE(cache_read_input_tokens, 0), 0),
|
||||
0
|
||||
)
|
||||
ELSE GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
END
|
||||
),
|
||||
0
|
||||
) AS BIGINT
|
||||
) AS effective_input_tokens,
|
||||
CAST(COALESCE(SUM(effective_input_tokens), 0) AS BIGINT) AS effective_input_tokens,
|
||||
CAST(COALESCE(SUM(output_tokens), 0) AS BIGINT) AS output_tokens,
|
||||
CAST(COALESCE(SUM(total_tokens), 0) AS BIGINT) AS total_tokens,
|
||||
CAST(
|
||||
COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN COALESCE(cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE(cache_creation_input_tokens, 0)
|
||||
END
|
||||
),
|
||||
0
|
||||
) AS BIGINT
|
||||
) AS cache_creation_tokens,
|
||||
CAST(COALESCE(SUM(cache_creation_input_tokens), 0) AS BIGINT) AS cache_creation_tokens,
|
||||
CAST(COALESCE(SUM(cache_creation_input_tokens_5m), 0) AS BIGINT)
|
||||
AS cache_creation_ephemeral_5m_tokens,
|
||||
CAST(COALESCE(SUM(cache_creation_input_tokens_1h), 0) AS BIGINT)
|
||||
AS cache_creation_ephemeral_1h_tokens,
|
||||
CAST(COALESCE(SUM(cache_read_input_tokens), 0) AS BIGINT) AS cache_read_tokens,
|
||||
CAST(
|
||||
COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN split_part(lower(COALESCE(COALESCE(endpoint_api_format, api_format), '')), ':', 1)
|
||||
IN ('claude', 'anthropic')
|
||||
THEN GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
+ CASE
|
||||
WHEN COALESCE(cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE(cache_creation_input_tokens, 0)
|
||||
END
|
||||
+ GREATEST(COALESCE(cache_read_input_tokens, 0), 0)
|
||||
WHEN split_part(lower(COALESCE(COALESCE(endpoint_api_format, api_format), '')), ':', 1)
|
||||
IN ('openai', 'gemini', 'google')
|
||||
THEN (
|
||||
CASE
|
||||
WHEN GREATEST(COALESCE(input_tokens, 0), 0) <= 0 THEN 0
|
||||
WHEN GREATEST(COALESCE(cache_read_input_tokens, 0), 0) <= 0
|
||||
THEN GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
ELSE GREATEST(
|
||||
GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
- GREATEST(COALESCE(cache_read_input_tokens, 0), 0),
|
||||
0
|
||||
)
|
||||
END
|
||||
) + GREATEST(COALESCE(cache_read_input_tokens, 0), 0)
|
||||
ELSE CASE
|
||||
WHEN (
|
||||
CASE
|
||||
WHEN COALESCE(cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE(cache_creation_input_tokens, 0)
|
||||
END
|
||||
) > 0
|
||||
THEN GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
+ (
|
||||
CASE
|
||||
WHEN COALESCE(cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE(cache_creation_input_tokens, 0)
|
||||
END
|
||||
)
|
||||
+ GREATEST(COALESCE(cache_read_input_tokens, 0), 0)
|
||||
ELSE GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
+ GREATEST(COALESCE(cache_read_input_tokens, 0), 0)
|
||||
END
|
||||
END
|
||||
),
|
||||
0
|
||||
) AS BIGINT
|
||||
) AS total_input_context,
|
||||
CAST(COALESCE(SUM(total_input_context), 0) AS BIGINT) AS total_input_context,
|
||||
CAST(COALESCE(SUM(total_cost_usd), 0) AS DOUBLE PRECISION) AS total_cost,
|
||||
CAST(COALESCE(SUM(actual_total_cost_usd), 0) AS DOUBLE PRECISION) AS actual_total_cost,
|
||||
COALESCE(
|
||||
@@ -2146,117 +1644,16 @@ WITH aggregated AS (
|
||||
) AS BIGINT
|
||||
) AS success_requests,
|
||||
CAST(COALESCE(SUM(input_tokens), 0) AS BIGINT) AS input_tokens,
|
||||
CAST(
|
||||
COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN GREATEST(COALESCE(input_tokens, 0), 0) <= 0 THEN 0
|
||||
WHEN GREATEST(COALESCE(cache_read_input_tokens, 0), 0) <= 0
|
||||
THEN GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
WHEN split_part(lower(COALESCE(COALESCE(endpoint_api_format, api_format), '')), ':', 1)
|
||||
IN ('openai', 'gemini', 'google')
|
||||
THEN GREATEST(
|
||||
GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
- GREATEST(COALESCE(cache_read_input_tokens, 0), 0),
|
||||
0
|
||||
)
|
||||
ELSE GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
END
|
||||
),
|
||||
0
|
||||
) AS BIGINT
|
||||
) AS effective_input_tokens,
|
||||
CAST(COALESCE(SUM(effective_input_tokens), 0) AS BIGINT) AS effective_input_tokens,
|
||||
CAST(COALESCE(SUM(output_tokens), 0) AS BIGINT) AS output_tokens,
|
||||
CAST(COALESCE(SUM(total_tokens), 0) AS BIGINT) AS total_tokens,
|
||||
CAST(
|
||||
COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN COALESCE(cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE(cache_creation_input_tokens, 0)
|
||||
END
|
||||
),
|
||||
0
|
||||
) AS BIGINT
|
||||
) AS cache_creation_tokens,
|
||||
CAST(COALESCE(SUM(cache_creation_input_tokens), 0) AS BIGINT) AS cache_creation_tokens,
|
||||
CAST(COALESCE(SUM(cache_creation_input_tokens_5m), 0) AS BIGINT)
|
||||
AS cache_creation_ephemeral_5m_tokens,
|
||||
CAST(COALESCE(SUM(cache_creation_input_tokens_1h), 0) AS BIGINT)
|
||||
AS cache_creation_ephemeral_1h_tokens,
|
||||
CAST(COALESCE(SUM(cache_read_input_tokens), 0) AS BIGINT) AS cache_read_tokens,
|
||||
CAST(
|
||||
COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN split_part(lower(COALESCE(COALESCE(endpoint_api_format, api_format), '')), ':', 1)
|
||||
IN ('claude', 'anthropic')
|
||||
THEN GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
+ CASE
|
||||
WHEN COALESCE(cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE(cache_creation_input_tokens, 0)
|
||||
END
|
||||
+ GREATEST(COALESCE(cache_read_input_tokens, 0), 0)
|
||||
WHEN split_part(lower(COALESCE(COALESCE(endpoint_api_format, api_format), '')), ':', 1)
|
||||
IN ('openai', 'gemini', 'google')
|
||||
THEN (
|
||||
CASE
|
||||
WHEN GREATEST(COALESCE(input_tokens, 0), 0) <= 0 THEN 0
|
||||
WHEN GREATEST(COALESCE(cache_read_input_tokens, 0), 0) <= 0
|
||||
THEN GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
ELSE GREATEST(
|
||||
GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
- GREATEST(COALESCE(cache_read_input_tokens, 0), 0),
|
||||
0
|
||||
)
|
||||
END
|
||||
) + GREATEST(COALESCE(cache_read_input_tokens, 0), 0)
|
||||
ELSE CASE
|
||||
WHEN (
|
||||
CASE
|
||||
WHEN COALESCE(cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE(cache_creation_input_tokens, 0)
|
||||
END
|
||||
) > 0
|
||||
THEN GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
+ (
|
||||
CASE
|
||||
WHEN COALESCE(cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE(cache_creation_input_tokens, 0)
|
||||
END
|
||||
)
|
||||
+ GREATEST(COALESCE(cache_read_input_tokens, 0), 0)
|
||||
ELSE GREATEST(COALESCE(input_tokens, 0), 0)
|
||||
+ GREATEST(COALESCE(cache_read_input_tokens, 0), 0)
|
||||
END
|
||||
END
|
||||
),
|
||||
0
|
||||
) AS BIGINT
|
||||
) AS total_input_context,
|
||||
CAST(COALESCE(SUM(total_input_context), 0) AS BIGINT) AS total_input_context,
|
||||
CAST(COALESCE(SUM(total_cost_usd), 0) AS DOUBLE PRECISION) AS total_cost,
|
||||
CAST(COALESCE(SUM(actual_total_cost_usd), 0) AS DOUBLE PRECISION) AS actual_total_cost,
|
||||
COALESCE(
|
||||
@@ -3009,7 +2406,13 @@ WHERE created_at >= $1
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::SELECT_STATS_DAILY_AGGREGATE_SQL;
|
||||
use super::{
|
||||
SELECT_STATS_DAILY_AGGREGATE_SQL, UPSERT_STATS_DAILY_API_KEY_SQL,
|
||||
UPSERT_STATS_DAILY_MODEL_PROVIDER_SQL, UPSERT_STATS_DAILY_MODEL_SQL,
|
||||
UPSERT_STATS_DAILY_PROVIDER_SQL, UPSERT_STATS_USER_DAILY_API_FORMAT_SQL,
|
||||
UPSERT_STATS_USER_DAILY_MODEL_PROVIDER_SQL, UPSERT_STATS_USER_DAILY_MODEL_SQL,
|
||||
UPSERT_STATS_USER_DAILY_PROVIDER_SQL, UPSERT_STATS_USER_DAILY_SQL,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn daily_aggregate_uses_one_time_bounded_base_table_scan() {
|
||||
@@ -3100,4 +2503,95 @@ mod tests {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn billing_facts_rollups_do_not_renormalize_token_columns() {
|
||||
let token_rollups = [
|
||||
("daily", SELECT_STATS_DAILY_AGGREGATE_SQL),
|
||||
("daily-model", UPSERT_STATS_DAILY_MODEL_SQL),
|
||||
("daily-provider", UPSERT_STATS_DAILY_PROVIDER_SQL),
|
||||
("daily-api-key", UPSERT_STATS_DAILY_API_KEY_SQL),
|
||||
("user-daily", UPSERT_STATS_USER_DAILY_SQL),
|
||||
("user-daily-model", UPSERT_STATS_USER_DAILY_MODEL_SQL),
|
||||
("user-daily-provider", UPSERT_STATS_USER_DAILY_PROVIDER_SQL),
|
||||
(
|
||||
"user-daily-api-format",
|
||||
UPSERT_STATS_USER_DAILY_API_FORMAT_SQL,
|
||||
),
|
||||
];
|
||||
|
||||
for (name, sql) in token_rollups {
|
||||
let normalized = sql.split_whitespace().collect::<Vec<_>>().join(" ");
|
||||
|
||||
assert!(
|
||||
normalized.contains("SUM(input_tokens)"),
|
||||
"{name} must aggregate the billing input column exposed by usage_billing_facts"
|
||||
);
|
||||
assert!(
|
||||
normalized.contains("SUM(cache_creation_input_tokens)"),
|
||||
"{name} must aggregate the canonical cache-creation column"
|
||||
);
|
||||
assert!(
|
||||
!normalized.contains("- GREATEST(COALESCE(cache_read_input_tokens"),
|
||||
"{name} must not subtract cache-read tokens from billing input again"
|
||||
);
|
||||
assert!(
|
||||
!normalized.contains("WHEN COALESCE(cache_creation_input_tokens, 0) = 0"),
|
||||
"{name} must not reconstruct canonical cache-creation tokens"
|
||||
);
|
||||
}
|
||||
|
||||
for (name, sql) in [
|
||||
("daily", SELECT_STATS_DAILY_AGGREGATE_SQL),
|
||||
("daily-api-key", UPSERT_STATS_DAILY_API_KEY_SQL),
|
||||
("user-daily", UPSERT_STATS_USER_DAILY_SQL),
|
||||
("user-daily-model", UPSERT_STATS_USER_DAILY_MODEL_SQL),
|
||||
("user-daily-provider", UPSERT_STATS_USER_DAILY_PROVIDER_SQL),
|
||||
(
|
||||
"user-daily-api-format",
|
||||
UPSERT_STATS_USER_DAILY_API_FORMAT_SQL,
|
||||
),
|
||||
] {
|
||||
assert!(
|
||||
sql.contains("SUM(effective_input_tokens)"),
|
||||
"{name} must aggregate usage_billing_facts.effective_input_tokens"
|
||||
);
|
||||
}
|
||||
|
||||
for (name, sql) in [
|
||||
("daily", SELECT_STATS_DAILY_AGGREGATE_SQL),
|
||||
("user-daily", UPSERT_STATS_USER_DAILY_SQL),
|
||||
("user-daily-model", UPSERT_STATS_USER_DAILY_MODEL_SQL),
|
||||
("user-daily-provider", UPSERT_STATS_USER_DAILY_PROVIDER_SQL),
|
||||
(
|
||||
"user-daily-api-format",
|
||||
UPSERT_STATS_USER_DAILY_API_FORMAT_SQL,
|
||||
),
|
||||
] {
|
||||
assert!(
|
||||
sql.contains("SUM(total_input_context)"),
|
||||
"{name} must aggregate usage_billing_facts.total_input_context"
|
||||
);
|
||||
}
|
||||
|
||||
for (name, sql) in [
|
||||
(
|
||||
"daily-model-provider",
|
||||
UPSERT_STATS_DAILY_MODEL_PROVIDER_SQL,
|
||||
),
|
||||
(
|
||||
"user-daily-model-provider",
|
||||
UPSERT_STATS_USER_DAILY_MODEL_PROVIDER_SQL,
|
||||
),
|
||||
] {
|
||||
assert!(
|
||||
sql.contains("SUM(total_tokens)"),
|
||||
"{name} must aggregate usage_billing_facts.total_tokens"
|
||||
);
|
||||
assert!(
|
||||
!sql.contains("- GREATEST(COALESCE(cache_read_input_tokens"),
|
||||
"{name} must not derive total tokens by subtracting cache reads"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,18 +37,8 @@ SELECT
|
||||
) AS completed_input_tokens,
|
||||
CAST(
|
||||
COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN COALESCE(usage.cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE(usage.cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(usage.cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE(usage.cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(usage.cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE(usage.cache_creation_input_tokens, 0)
|
||||
END
|
||||
) FILTER (WHERE usage.status = 'completed'),
|
||||
SUM(GREATEST(COALESCE(usage.cache_creation_input_tokens, 0), 0))
|
||||
FILTER (WHERE usage.status = 'completed'),
|
||||
0
|
||||
) AS BIGINT
|
||||
) AS completed_cache_creation_tokens,
|
||||
@@ -61,85 +51,8 @@ SELECT
|
||||
) AS completed_cache_read_tokens,
|
||||
CAST(
|
||||
COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN split_part(
|
||||
lower(
|
||||
COALESCE(
|
||||
COALESCE(usage.endpoint_api_format, usage.api_format),
|
||||
''
|
||||
)
|
||||
),
|
||||
':',
|
||||
1
|
||||
) IN ('claude', 'anthropic')
|
||||
THEN GREATEST(COALESCE(usage.input_tokens, 0), 0)
|
||||
+ CASE
|
||||
WHEN COALESCE(usage.cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE(usage.cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(usage.cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE(usage.cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(usage.cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE(usage.cache_creation_input_tokens, 0)
|
||||
END
|
||||
+ GREATEST(COALESCE(usage.cache_read_input_tokens, 0), 0)
|
||||
WHEN split_part(
|
||||
lower(
|
||||
COALESCE(
|
||||
COALESCE(usage.endpoint_api_format, usage.api_format),
|
||||
''
|
||||
)
|
||||
),
|
||||
':',
|
||||
1
|
||||
) IN ('openai', 'gemini', 'google')
|
||||
THEN (
|
||||
CASE
|
||||
WHEN GREATEST(COALESCE(usage.input_tokens, 0), 0) <= 0
|
||||
THEN 0
|
||||
WHEN GREATEST(COALESCE(usage.cache_read_input_tokens, 0), 0) <= 0
|
||||
THEN GREATEST(COALESCE(usage.input_tokens, 0), 0)
|
||||
ELSE GREATEST(
|
||||
GREATEST(COALESCE(usage.input_tokens, 0), 0)
|
||||
- GREATEST(COALESCE(usage.cache_read_input_tokens, 0), 0),
|
||||
0
|
||||
)
|
||||
END
|
||||
) + GREATEST(COALESCE(usage.cache_read_input_tokens, 0), 0)
|
||||
ELSE CASE
|
||||
WHEN (
|
||||
CASE
|
||||
WHEN COALESCE(usage.cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE(usage.cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(usage.cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE(usage.cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(usage.cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE(usage.cache_creation_input_tokens, 0)
|
||||
END
|
||||
) > 0
|
||||
THEN GREATEST(COALESCE(usage.input_tokens, 0), 0)
|
||||
+ (
|
||||
CASE
|
||||
WHEN COALESCE(usage.cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE(usage.cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(usage.cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE(usage.cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(usage.cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE(usage.cache_creation_input_tokens, 0)
|
||||
END
|
||||
)
|
||||
+ GREATEST(COALESCE(usage.cache_read_input_tokens, 0), 0)
|
||||
ELSE GREATEST(COALESCE(usage.input_tokens, 0), 0)
|
||||
+ GREATEST(COALESCE(usage.cache_read_input_tokens, 0), 0)
|
||||
END
|
||||
END
|
||||
) FILTER (WHERE usage.status = 'completed'),
|
||||
SUM(GREATEST(COALESCE(usage.total_input_context, 0), 0))
|
||||
FILTER (WHERE usage.status = 'completed'),
|
||||
0
|
||||
) AS BIGINT
|
||||
) AS completed_total_input_context,
|
||||
@@ -255,24 +168,15 @@ SELECT
|
||||
0
|
||||
) AS BIGINT
|
||||
) AS output_tokens,
|
||||
CAST(COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN COALESCE(usage.cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE(usage.cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(usage.cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE(usage.cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(usage.cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE(usage.cache_creation_input_tokens, 0)
|
||||
END
|
||||
) FILTER (
|
||||
WHERE usage.status NOT IN ('pending', 'streaming')
|
||||
AND usage.provider_name NOT IN ('unknown', 'pending')
|
||||
),
|
||||
0
|
||||
) AS BIGINT) AS cache_creation_tokens,
|
||||
CAST(
|
||||
COALESCE(
|
||||
SUM(GREATEST(COALESCE(usage.cache_creation_input_tokens, 0), 0)) FILTER (
|
||||
WHERE usage.status NOT IN ('pending', 'streaming')
|
||||
AND usage.provider_name NOT IN ('unknown', 'pending')
|
||||
),
|
||||
0
|
||||
) AS BIGINT
|
||||
) AS cache_creation_tokens,
|
||||
CAST(
|
||||
COALESCE(
|
||||
SUM(usage.cache_read_input_tokens) FILTER (
|
||||
@@ -439,18 +343,7 @@ WITH aggregated AS (
|
||||
CAST(COALESCE(SUM(input_tokens), 0) AS BIGINT) AS input_tokens,
|
||||
CAST(COALESCE(SUM(output_tokens), 0) AS BIGINT) AS output_tokens,
|
||||
CAST(COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN COALESCE(cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE(cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE(cache_creation_input_tokens, 0)
|
||||
END
|
||||
),
|
||||
SUM(GREATEST(COALESCE(cache_creation_input_tokens, 0), 0)),
|
||||
0
|
||||
) AS BIGINT) AS cache_creation_tokens,
|
||||
CAST(COALESCE(SUM(cache_read_input_tokens), 0) AS BIGINT) AS cache_read_tokens,
|
||||
@@ -846,7 +739,7 @@ DO UPDATE SET
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::SELECT_STATS_HOURLY_AGGREGATE_SQL;
|
||||
use super::{SELECT_STATS_HOURLY_AGGREGATE_SQL, UPSERT_STATS_HOURLY_USER_SQL};
|
||||
|
||||
#[test]
|
||||
fn hourly_aggregate_uses_one_time_bounded_base_table_scan() {
|
||||
@@ -938,4 +831,32 @@ mod tests {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hourly_aggregates_do_not_renormalize_billing_fact_tokens() {
|
||||
let aggregate_sql = SELECT_STATS_HOURLY_AGGREGATE_SQL;
|
||||
let user_sql = UPSERT_STATS_HOURLY_USER_SQL;
|
||||
|
||||
assert_eq!(
|
||||
aggregate_sql.matches("usage.total_input_context").count(),
|
||||
1
|
||||
);
|
||||
assert!(aggregate_sql.contains(
|
||||
"SUM(GREATEST(COALESCE(usage.total_input_context, 0), 0))\n FILTER (WHERE usage.status = 'completed')"
|
||||
));
|
||||
assert_eq!(
|
||||
aggregate_sql
|
||||
.matches("SUM(GREATEST(COALESCE(usage.cache_creation_input_tokens, 0), 0))")
|
||||
.count(),
|
||||
3
|
||||
);
|
||||
assert!(user_sql.contains("SUM(GREATEST(COALESCE(cache_creation_input_tokens, 0), 0))"));
|
||||
|
||||
for sql in [aggregate_sql, user_sql] {
|
||||
assert!(!sql.contains("split_part("));
|
||||
assert!(!sql.contains("cache_creation_input_tokens_5m"));
|
||||
assert!(!sql.contains("cache_creation_input_tokens_1h"));
|
||||
assert!(!sql.contains("- GREATEST(COALESCE("));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,8 @@ fn pending_backfills_from_applied_returns_all_versions_when_none_applied() {
|
||||
20260422120000,
|
||||
20260504120000,
|
||||
20260505120000,
|
||||
20260517012000
|
||||
20260517012000,
|
||||
20260716010000
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -47,7 +48,8 @@ fn pending_backfills_from_applied_skips_versions_already_applied() {
|
||||
20260422120000,
|
||||
20260504120000,
|
||||
20260505120000,
|
||||
20260517012000
|
||||
20260517012000,
|
||||
20260716010000
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -184,7 +186,7 @@ impl ManagedPostgresServer {
|
||||
.arg("-c")
|
||||
.arg("max_connections=8")
|
||||
.arg("-c")
|
||||
.arg("dynamic_shared_memory_type=none")
|
||||
.arg("dynamic_shared_memory_type=mmap")
|
||||
.arg("-c")
|
||||
.arg("autovacuum=off")
|
||||
.stdout(Stdio::from(stdout))
|
||||
@@ -417,12 +419,13 @@ async fn run_backfills_rebuilds_stats_and_records_execution() {
|
||||
let pending_before = pending_backfills(&pool)
|
||||
.await
|
||||
.expect("pending backfills should load");
|
||||
assert_eq!(pending_before.len(), 5);
|
||||
assert_eq!(pending_before.len(), 6);
|
||||
assert_eq!(pending_before[0].version, 20260422110000);
|
||||
assert_eq!(pending_before[1].version, 20260422120000);
|
||||
assert_eq!(pending_before[2].version, 20260504120000);
|
||||
assert_eq!(pending_before[3].version, 20260505120000);
|
||||
assert_eq!(pending_before[4].version, 20260517012000);
|
||||
assert_eq!(pending_before[5].version, 20260716010000);
|
||||
|
||||
run_backfills(&pool)
|
||||
.await
|
||||
@@ -445,7 +448,8 @@ async fn run_backfills_rebuilds_stats_and_records_execution() {
|
||||
20260422120000,
|
||||
20260504120000,
|
||||
20260505120000,
|
||||
20260517012000
|
||||
20260517012000,
|
||||
20260716010000
|
||||
]
|
||||
);
|
||||
|
||||
@@ -841,6 +845,25 @@ async fn run_backfills_rebuilds_stats_and_records_execution() {
|
||||
.expect("user daily model-provider stats should load");
|
||||
assert_eq!(user_daily_model_provider_requests, 1);
|
||||
|
||||
let canonical_rollup_totals: Vec<i64> = query_scalar(
|
||||
r#"
|
||||
SELECT total_tokens FROM public.stats_daily_model_provider
|
||||
UNION ALL
|
||||
SELECT total_tokens FROM public.stats_user_daily_model
|
||||
UNION ALL
|
||||
SELECT total_tokens FROM public.stats_user_daily_provider
|
||||
UNION ALL
|
||||
SELECT total_tokens FROM public.stats_user_daily_api_format
|
||||
UNION ALL
|
||||
SELECT total_tokens FROM public.stats_user_daily_model_provider
|
||||
ORDER BY total_tokens
|
||||
"#,
|
||||
)
|
||||
.fetch_all(&pool)
|
||||
.await
|
||||
.expect("canonical persisted rollup totals should load");
|
||||
assert_eq!(canonical_rollup_totals, vec![150, 150, 150, 150, 150]);
|
||||
|
||||
let user_daily_ephemeral_5m_tokens: i64 = query_scalar(
|
||||
"SELECT COALESCE(SUM(cache_creation_ephemeral_5m_tokens), 0)::BIGINT FROM public.stats_user_daily",
|
||||
)
|
||||
@@ -1100,5 +1123,5 @@ async fn run_backfills_rebuilds_stats_and_records_execution() {
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
.expect("backfill count should load");
|
||||
assert_eq!(applied_count, 5);
|
||||
assert_eq!(applied_count, 6);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use tracing::info;
|
||||
// Generated by build.rs from schema/bootstrap/postgres.
|
||||
pub(crate) static EMPTY_DATABASE_SNAPSHOT_SQL: &str =
|
||||
include_str!(concat!(env!("OUT_DIR"), "/empty_database_snapshot.sql"));
|
||||
pub(crate) const EMPTY_DATABASE_SNAPSHOT_CUTOFF_VERSION: i64 = 20260715130100;
|
||||
pub(crate) const EMPTY_DATABASE_SNAPSHOT_CUTOFF_VERSION: i64 = 20260716000000;
|
||||
|
||||
const PUBLIC_BASE_TABLE_COUNT_SQL: &str = r#"
|
||||
SELECT COUNT(*)::BIGINT
|
||||
|
||||
@@ -325,6 +325,7 @@ fn empty_database_snapshot_covers_current_cutoff_versions() {
|
||||
20260715000000,
|
||||
20260715130000,
|
||||
20260715130100,
|
||||
20260716000000,
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -1577,6 +1578,7 @@ fn pending_migrations_from_applied_skips_versions_already_applied() {
|
||||
20260715000000,
|
||||
20260715130000,
|
||||
20260715130100,
|
||||
20260716000000,
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -2119,6 +2121,210 @@ async fn prepare_database_for_startup_bootstraps_clean_database() {
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn postgres_usage_billing_facts_total_tokens_counts_cached_input_once() {
|
||||
const LEGACY_VIEW_MIGRATION_VERSION: i64 = 20260505130000;
|
||||
const FIX_MIGRATION_VERSION: i64 = 20260716000000;
|
||||
|
||||
let Some(server) = ManagedPostgresServer::try_start()
|
||||
.await
|
||||
.expect("postgres billing-facts migration test should start or skip")
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let pool = PgPool::connect(server.database_url())
|
||||
.await
|
||||
.expect("pool should connect");
|
||||
let pending = prepare_database_for_startup(&pool)
|
||||
.await
|
||||
.expect("clean database bootstrap should succeed");
|
||||
assert!(pending.is_empty());
|
||||
|
||||
let legacy_view_migration = POSTGRES_MIGRATOR
|
||||
.iter()
|
||||
.find(|migration| migration.version == LEGACY_VIEW_MIGRATION_VERSION)
|
||||
.expect("legacy billing-facts view migration should be embedded");
|
||||
assert!(POSTGRES_MIGRATOR
|
||||
.iter()
|
||||
.any(|migration| migration.version == FIX_MIGRATION_VERSION));
|
||||
|
||||
query(
|
||||
r#"
|
||||
INSERT INTO public."usage" (
|
||||
id,
|
||||
request_id,
|
||||
api_key_id,
|
||||
provider_name,
|
||||
model,
|
||||
api_format,
|
||||
endpoint_api_format,
|
||||
input_tokens,
|
||||
output_tokens,
|
||||
cache_creation_input_tokens,
|
||||
cache_read_input_tokens,
|
||||
total_tokens,
|
||||
status,
|
||||
billing_status
|
||||
) VALUES
|
||||
(
|
||||
'billing-facts-cache-read',
|
||||
'billing-facts-cache-read',
|
||||
'billing-facts-api-key',
|
||||
'openai',
|
||||
'gpt-5',
|
||||
'openai:chat',
|
||||
'openai:chat',
|
||||
166103,
|
||||
94,
|
||||
0,
|
||||
164608,
|
||||
999999,
|
||||
'completed',
|
||||
'settled'
|
||||
),
|
||||
(
|
||||
'billing-facts-cache-create',
|
||||
'billing-facts-cache-create',
|
||||
'billing-facts-api-key',
|
||||
'openai',
|
||||
'gpt-5',
|
||||
'openai:chat',
|
||||
'openai:chat',
|
||||
1000,
|
||||
20,
|
||||
100,
|
||||
400,
|
||||
999999,
|
||||
'completed',
|
||||
'settled'
|
||||
),
|
||||
(
|
||||
'billing-facts-legacy',
|
||||
'billing-facts-legacy',
|
||||
'billing-facts-legacy-api-key',
|
||||
'legacy',
|
||||
'legacy-model',
|
||||
NULL,
|
||||
NULL,
|
||||
50,
|
||||
27,
|
||||
0,
|
||||
0,
|
||||
77,
|
||||
'completed',
|
||||
'settled'
|
||||
)
|
||||
"#,
|
||||
)
|
||||
.execute(&pool)
|
||||
.await
|
||||
.expect("usage fixtures should be inserted");
|
||||
|
||||
query(
|
||||
r#"
|
||||
INSERT INTO public.usage_settlement_snapshots (
|
||||
request_id,
|
||||
billing_status,
|
||||
billing_input_tokens,
|
||||
billing_effective_input_tokens,
|
||||
billing_output_tokens,
|
||||
billing_cache_creation_tokens,
|
||||
billing_cache_read_tokens,
|
||||
billing_total_input_context
|
||||
) VALUES
|
||||
(
|
||||
'billing-facts-cache-read',
|
||||
'settled',
|
||||
166103,
|
||||
1495,
|
||||
94,
|
||||
0,
|
||||
164608,
|
||||
166103
|
||||
),
|
||||
(
|
||||
'billing-facts-cache-create',
|
||||
'settled',
|
||||
1000,
|
||||
500,
|
||||
20,
|
||||
100,
|
||||
400,
|
||||
900
|
||||
)
|
||||
"#,
|
||||
)
|
||||
.execute(&pool)
|
||||
.await
|
||||
.expect("settlement fixtures should be inserted");
|
||||
|
||||
sqlx::raw_sql(legacy_view_migration.sql.as_ref())
|
||||
.execute(&pool)
|
||||
.await
|
||||
.expect("legacy billing-facts view should be restored for the upgrade fixture");
|
||||
let duplicated_total: i64 = query_scalar(
|
||||
"SELECT total_tokens FROM public.usage_billing_facts WHERE request_id = 'billing-facts-cache-read'",
|
||||
)
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
.expect("legacy billing-facts total should be readable");
|
||||
assert_eq!(duplicated_total, 330805);
|
||||
|
||||
query("DELETE FROM public._sqlx_migrations WHERE version = $1")
|
||||
.bind(FIX_MIGRATION_VERSION)
|
||||
.execute(&pool)
|
||||
.await
|
||||
.expect("billing-facts fix migration stamp should be reset");
|
||||
super::run_migrations(&pool)
|
||||
.await
|
||||
.expect("billing-facts fix migration should rebuild the view");
|
||||
|
||||
let cache_read_facts: (i64, i64, i64, i64, i64) = sqlx::query_as(
|
||||
r#"
|
||||
SELECT
|
||||
effective_input_tokens,
|
||||
cache_creation_input_tokens,
|
||||
cache_read_input_tokens,
|
||||
total_input_context,
|
||||
total_tokens
|
||||
FROM public.usage_billing_facts
|
||||
WHERE request_id = 'billing-facts-cache-read'
|
||||
"#,
|
||||
)
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
.expect("canonical cache-read billing facts should be readable");
|
||||
assert_eq!(cache_read_facts, (1495, 0, 164608, 166103, 166197));
|
||||
|
||||
let cache_creation_facts: (i64, i64) = sqlx::query_as(
|
||||
r#"
|
||||
SELECT total_input_context, total_tokens
|
||||
FROM public.usage_billing_facts
|
||||
WHERE request_id = 'billing-facts-cache-create'
|
||||
"#,
|
||||
)
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
.expect("canonical cache-creation billing facts should be readable");
|
||||
assert_eq!(cache_creation_facts, (1000, 1020));
|
||||
|
||||
let legacy_total: i64 = query_scalar(
|
||||
"SELECT total_tokens FROM public.usage_billing_facts WHERE request_id = 'billing-facts-legacy'",
|
||||
)
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
.expect("legacy billing-facts total should be readable");
|
||||
assert_eq!(legacy_total, 77);
|
||||
|
||||
let repository = aether_data_postgres::SqlxUsageReadRepository::new(pool.clone());
|
||||
let totals = repository
|
||||
.summarize_total_tokens_by_api_key_ids(&["billing-facts-api-key".to_string()])
|
||||
.await
|
||||
.expect("API-key totals should use canonical billing facts");
|
||||
assert_eq!(totals.get("billing-facts-api-key"), Some(&167217));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn postgres_migrations_repair_invalid_concurrent_cleanup_index() {
|
||||
const MIGRATION_VERSION: i64 = 20260715000000;
|
||||
|
||||
Reference in New Issue
Block a user