mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-01 17:00:21 +08:00
feat: 扩展 cache creation token 细分统计与 effective_input_tokens 计费逻辑
- 新增 cache_creation_ephemeral_5m/1h_input_tokens 字段,区分不同 TTL 的缓存写入 token - 引入 effective_input_tokens(扣除 cache read 后的有效输入 token),暴露给 usage 接口 - billing 规则生成器支持 5m/1h ephemeral cache 独立定价与分级计费 - usage_mapper 增加 Claude/Anthropic 格式映射,修复 OpenAI responses 格式字段兼容性 - 迁移逻辑增强:支持 checksum 容错、applied/pending 数量日志、逐步执行信息输出 - executor 抽离 LocalExecutionRequestOutcome 类型,统一 sync/stream 路径返回语义 - provider-transport auth 层新增 complete passthrough headers 构建逻辑 - 前端 usage 类型全面补充 effective_input_tokens、cache_creation_tokens、total_input_context 字段
This commit is contained in:
@@ -20,5 +20,6 @@ sha2.workspace = true
|
||||
sqlx = { workspace = true, features = ["migrate", "macros"] }
|
||||
thiserror.workspace = true
|
||||
tokio.workspace = true
|
||||
tracing.workspace = true
|
||||
url.workspace = true
|
||||
uuid.workspace = true
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
-- Align Rust-managed migrations with legacy Alembic revision
|
||||
-- c3d4e5f6a7b8 (usage_token_semantics_v2).
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (
|
||||
SELECT 1
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name = 'usage'
|
||||
AND column_name = 'total_tokens'
|
||||
) AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name = 'usage'
|
||||
AND column_name = 'input_output_total_tokens'
|
||||
) THEN
|
||||
ALTER TABLE "usage" RENAME COLUMN total_tokens TO input_output_total_tokens;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
ALTER TABLE "usage"
|
||||
ADD COLUMN IF NOT EXISTS input_output_total_tokens INTEGER DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS cache_creation_input_tokens_5m INTEGER NOT NULL DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS cache_creation_input_tokens_1h INTEGER NOT NULL DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS input_context_tokens INTEGER NOT NULL DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS total_tokens INTEGER NOT NULL DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS cache_creation_cost_usd_5m NUMERIC(20, 8) NOT NULL DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS cache_creation_cost_usd_1h NUMERIC(20, 8) NOT NULL DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS actual_cache_creation_cost_usd_5m NUMERIC(20, 8) NOT NULL DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS actual_cache_creation_cost_usd_1h NUMERIC(20, 8) NOT NULL DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS actual_cache_cost_usd NUMERIC(20, 8) NOT NULL DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS cache_creation_price_per_1m_5m NUMERIC(20, 8),
|
||||
ADD COLUMN IF NOT EXISTS cache_creation_price_per_1m_1h NUMERIC(20, 8);
|
||||
|
||||
UPDATE "usage"
|
||||
SET
|
||||
input_output_total_tokens = src.new_iot,
|
||||
input_context_tokens = src.new_ict,
|
||||
total_tokens = src.new_total,
|
||||
cache_creation_cost_usd_5m = src.new_cc5m,
|
||||
cache_creation_cost_usd_1h = src.new_cc1h,
|
||||
actual_cache_creation_cost_usd_5m = src.new_acc5m,
|
||||
actual_cache_creation_cost_usd_1h = src.new_acc1h,
|
||||
actual_cache_cost_usd = src.new_accu,
|
||||
cache_creation_price_per_1m_5m = src.new_cp5m,
|
||||
cache_creation_price_per_1m_1h = src.new_cp1h,
|
||||
cache_cost_usd = src.new_ccu
|
||||
FROM (
|
||||
SELECT
|
||||
id,
|
||||
COALESCE(
|
||||
input_output_total_tokens,
|
||||
COALESCE(input_tokens, 0) + COALESCE(output_tokens, 0)
|
||||
) AS new_iot,
|
||||
COALESCE(input_tokens, 0) + COALESCE(cache_read_input_tokens, 0) AS new_ict,
|
||||
COALESCE(
|
||||
input_output_total_tokens,
|
||||
COALESCE(input_tokens, 0) + COALESCE(output_tokens, 0)
|
||||
) + COALESCE(cache_creation_input_tokens, 0) + COALESCE(cache_read_input_tokens, 0) AS new_total,
|
||||
CASE
|
||||
WHEN COALESCE(cache_creation_input_tokens_5m, 0) > 0
|
||||
AND COALESCE(cache_creation_input_tokens_1h, 0) = 0
|
||||
THEN COALESCE(cache_creation_cost_usd, 0)
|
||||
WHEN COALESCE(cache_creation_input_tokens_5m, 0) > 0
|
||||
AND COALESCE(cache_creation_input_tokens, 0) > 0
|
||||
THEN COALESCE(cache_creation_cost_usd, 0)
|
||||
* (COALESCE(cache_creation_input_tokens_5m, 0) * 1.0
|
||||
/ GREATEST(COALESCE(cache_creation_input_tokens, 0), 1))
|
||||
ELSE 0
|
||||
END AS new_cc5m,
|
||||
CASE
|
||||
WHEN COALESCE(cache_creation_input_tokens_1h, 0) > 0
|
||||
AND COALESCE(cache_creation_input_tokens_5m, 0) = 0
|
||||
THEN COALESCE(cache_creation_cost_usd, 0)
|
||||
WHEN COALESCE(cache_creation_input_tokens_1h, 0) > 0
|
||||
AND COALESCE(cache_creation_input_tokens, 0) > 0
|
||||
THEN COALESCE(cache_creation_cost_usd, 0)
|
||||
* (COALESCE(cache_creation_input_tokens_1h, 0) * 1.0
|
||||
/ GREATEST(COALESCE(cache_creation_input_tokens, 0), 1))
|
||||
ELSE 0
|
||||
END AS new_cc1h,
|
||||
CASE
|
||||
WHEN COALESCE(cache_creation_input_tokens_5m, 0) > 0
|
||||
AND COALESCE(cache_creation_input_tokens_1h, 0) = 0
|
||||
THEN COALESCE(actual_cache_creation_cost_usd, 0)
|
||||
WHEN COALESCE(cache_creation_input_tokens_5m, 0) > 0
|
||||
AND COALESCE(cache_creation_input_tokens, 0) > 0
|
||||
THEN COALESCE(actual_cache_creation_cost_usd, 0)
|
||||
* (COALESCE(cache_creation_input_tokens_5m, 0) * 1.0
|
||||
/ GREATEST(COALESCE(cache_creation_input_tokens, 0), 1))
|
||||
ELSE 0
|
||||
END AS new_acc5m,
|
||||
CASE
|
||||
WHEN COALESCE(cache_creation_input_tokens_1h, 0) > 0
|
||||
AND COALESCE(cache_creation_input_tokens_5m, 0) = 0
|
||||
THEN COALESCE(actual_cache_creation_cost_usd, 0)
|
||||
WHEN COALESCE(cache_creation_input_tokens_1h, 0) > 0
|
||||
AND COALESCE(cache_creation_input_tokens, 0) > 0
|
||||
THEN COALESCE(actual_cache_creation_cost_usd, 0)
|
||||
* (COALESCE(cache_creation_input_tokens_1h, 0) * 1.0
|
||||
/ GREATEST(COALESCE(cache_creation_input_tokens, 0), 1))
|
||||
ELSE 0
|
||||
END AS new_acc1h,
|
||||
COALESCE(actual_cache_creation_cost_usd, 0)
|
||||
+ COALESCE(actual_cache_read_cost_usd, 0) AS new_accu,
|
||||
CASE
|
||||
WHEN COALESCE(cache_creation_input_tokens_5m, 0) > 0
|
||||
AND COALESCE(cache_creation_input_tokens_1h, 0) = 0
|
||||
THEN cache_creation_price_per_1m
|
||||
ELSE NULL
|
||||
END AS new_cp5m,
|
||||
CASE
|
||||
WHEN COALESCE(cache_creation_input_tokens_1h, 0) > 0
|
||||
AND COALESCE(cache_creation_input_tokens_5m, 0) = 0
|
||||
THEN cache_creation_price_per_1m
|
||||
ELSE NULL
|
||||
END AS new_cp1h,
|
||||
COALESCE(cache_creation_cost_usd, 0)
|
||||
+ COALESCE(cache_read_cost_usd, 0) AS new_ccu
|
||||
FROM "usage"
|
||||
) AS src
|
||||
WHERE "usage".id = src.id;
|
||||
22
crates/aether-data/schema/README.md
Normal file
22
crates/aether-data/schema/README.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Public Schema Snapshot
|
||||
|
||||
This directory stores a searchable snapshot of the current local Postgres `public` schema.
|
||||
|
||||
Purpose:
|
||||
- Make legacy Python-era columns discoverable without reverse-tracing every migration.
|
||||
- Keep `migrations/20260403000000_baseline.sql` as a no-op handoff point instead of stuffing the full legacy schema into a fake baseline.
|
||||
|
||||
Files:
|
||||
- `current-public-tables.tsv`: `table_name`, `column_count`
|
||||
- `current-public-columns.tsv`: `table_name`, `ordinal_position`, `column_name`, `data_type`, `is_nullable`, `column_default`, `column_comment`
|
||||
|
||||
Source:
|
||||
- Generated from the local `aether` Postgres database on `2026-04-10`.
|
||||
|
||||
Refresh commands:
|
||||
```bash
|
||||
docker compose exec -T postgres psql -U postgres -d aether -At -F $'\t' -c "SELECT table_name, COUNT(*) FROM information_schema.columns WHERE table_schema = 'public' GROUP BY table_name ORDER BY table_name;"
|
||||
docker compose exec -T postgres psql -U postgres -d aether -At -F $'\t' -c "SELECT c.table_name, c.ordinal_position, c.column_name, c.data_type, c.is_nullable, COALESCE(c.column_default, ''), COALESCE(pg_catalog.col_description((quote_ident(c.table_schema) || '.' || quote_ident(c.table_name))::regclass::oid, c.ordinal_position), '') FROM information_schema.columns c WHERE c.table_schema = 'public' ORDER BY c.table_name, c.ordinal_position;"
|
||||
```
|
||||
|
||||
This snapshot is documentation only. Runtime code must still treat the actual database as source of truth.
|
||||
831
crates/aether-data/schema/current-public-columns.tsv
Normal file
831
crates/aether-data/schema/current-public-columns.tsv
Normal file
@@ -0,0 +1,831 @@
|
||||
table_name ordinal_position column_name data_type is_nullable column_default column_comment
|
||||
_orphan_api_keys_backup 1 id character varying YES
|
||||
_orphan_api_keys_backup 2 api_key character varying YES
|
||||
_orphan_api_keys_backup 3 name character varying YES
|
||||
_orphan_api_keys_backup 4 note character varying YES
|
||||
_orphan_api_keys_backup 5 rate_multiplier double precision YES
|
||||
_orphan_api_keys_backup 6 internal_priority integer YES
|
||||
_orphan_api_keys_backup 7 global_priority integer YES
|
||||
_orphan_api_keys_backup 8 max_concurrent integer YES
|
||||
_orphan_api_keys_backup 9 allowed_models json YES
|
||||
_orphan_api_keys_backup 10 capabilities json YES
|
||||
_orphan_api_keys_backup 11 learned_max_concurrent integer YES
|
||||
_orphan_api_keys_backup 12 concurrent_429_count integer YES
|
||||
_orphan_api_keys_backup 13 rpm_429_count integer YES
|
||||
_orphan_api_keys_backup 14 last_429_at timestamp with time zone YES
|
||||
_orphan_api_keys_backup 15 last_429_type character varying YES
|
||||
_orphan_api_keys_backup 16 last_concurrent_peak integer YES
|
||||
_orphan_api_keys_backup 17 adjustment_history json YES
|
||||
_orphan_api_keys_backup 18 utilization_samples json YES
|
||||
_orphan_api_keys_backup 19 last_probe_increase_at timestamp with time zone YES
|
||||
_orphan_api_keys_backup 20 cache_ttl_minutes integer YES
|
||||
_orphan_api_keys_backup 21 max_probe_interval_minutes integer YES
|
||||
_orphan_api_keys_backup 22 request_count integer YES
|
||||
_orphan_api_keys_backup 23 success_count integer YES
|
||||
_orphan_api_keys_backup 24 error_count integer YES
|
||||
_orphan_api_keys_backup 25 total_response_time_ms integer YES
|
||||
_orphan_api_keys_backup 26 last_used_at timestamp with time zone YES
|
||||
_orphan_api_keys_backup 27 last_error_at timestamp with time zone YES
|
||||
_orphan_api_keys_backup 28 last_error_msg text YES
|
||||
_orphan_api_keys_backup 29 is_active boolean YES
|
||||
_orphan_api_keys_backup 30 expires_at timestamp with time zone YES
|
||||
_orphan_api_keys_backup 31 created_at timestamp with time zone YES
|
||||
_orphan_api_keys_backup 32 updated_at timestamp with time zone YES
|
||||
_orphan_api_keys_backup 33 endpoint_id character varying YES
|
||||
_orphan_api_keys_backup 34 rate_limit integer YES
|
||||
_orphan_api_keys_backup 35 daily_limit integer YES
|
||||
_orphan_api_keys_backup 36 monthly_limit integer YES
|
||||
_orphan_api_keys_backup 37 provider_id character varying YES
|
||||
_orphan_api_keys_backup 38 backup_at timestamp with time zone YES
|
||||
_sqlx_migrations 1 version bigint NO
|
||||
_sqlx_migrations 2 description text NO
|
||||
_sqlx_migrations 3 installed_on timestamp with time zone NO now()
|
||||
_sqlx_migrations 4 success boolean NO
|
||||
_sqlx_migrations 5 checksum bytea NO
|
||||
_sqlx_migrations 6 execution_time bigint NO
|
||||
alembic_version 1 version_num character varying NO
|
||||
announcement_reads 1 id character varying NO
|
||||
announcement_reads 2 user_id character varying NO
|
||||
announcement_reads 3 announcement_id character varying NO
|
||||
announcement_reads 4 read_at timestamp with time zone NO now()
|
||||
announcements 1 id character varying NO
|
||||
announcements 2 title character varying NO
|
||||
announcements 3 content text NO
|
||||
announcements 4 type character varying YES 'info'::character varying
|
||||
announcements 5 priority integer YES 0
|
||||
announcements 6 author_id character varying YES
|
||||
announcements 7 is_active boolean YES true
|
||||
announcements 8 is_pinned boolean YES false
|
||||
announcements 9 start_time timestamp with time zone YES
|
||||
announcements 10 end_time timestamp with time zone YES
|
||||
announcements 11 created_at timestamp with time zone NO now()
|
||||
announcements 12 updated_at timestamp with time zone NO now()
|
||||
api_key_provider_mappings 1 id character varying NO
|
||||
api_key_provider_mappings 2 api_key_id character varying NO
|
||||
api_key_provider_mappings 3 provider_id character varying NO
|
||||
api_key_provider_mappings 4 priority_adjustment integer YES 0
|
||||
api_key_provider_mappings 5 weight_multiplier double precision YES '1'::double precision
|
||||
api_key_provider_mappings 6 is_enabled boolean NO true
|
||||
api_key_provider_mappings 7 created_at timestamp with time zone NO now()
|
||||
api_key_provider_mappings 8 updated_at timestamp with time zone NO now()
|
||||
api_keys 1 id character varying NO
|
||||
api_keys 2 user_id character varying NO
|
||||
api_keys 3 key_hash character varying NO
|
||||
api_keys 4 key_encrypted text YES
|
||||
api_keys 5 name character varying YES
|
||||
api_keys 6 total_requests integer YES 0
|
||||
api_keys 7 total_cost_usd numeric YES '0'::double precision
|
||||
api_keys 10 is_standalone boolean NO false
|
||||
api_keys 11 allowed_providers json YES
|
||||
api_keys 13 allowed_api_formats json YES
|
||||
api_keys 14 allowed_models json YES
|
||||
api_keys 15 rate_limit integer YES 100
|
||||
api_keys 16 concurrent_limit integer YES 5
|
||||
api_keys 17 force_capabilities json YES
|
||||
api_keys 18 is_active boolean NO true
|
||||
api_keys 19 last_used_at timestamp with time zone YES
|
||||
api_keys 20 expires_at timestamp with time zone YES
|
||||
api_keys 21 auto_delete_on_expiry boolean NO false
|
||||
api_keys 22 created_at timestamp with time zone NO now()
|
||||
api_keys 23 updated_at timestamp with time zone NO now()
|
||||
api_keys 24 is_locked boolean NO false
|
||||
audit_logs 1 id character varying NO
|
||||
audit_logs 2 event_type character varying NO
|
||||
audit_logs 3 user_id character varying YES
|
||||
audit_logs 4 api_key_id character varying YES
|
||||
audit_logs 5 description text NO
|
||||
audit_logs 6 ip_address character varying YES
|
||||
audit_logs 7 user_agent character varying YES
|
||||
audit_logs 8 request_id character varying YES
|
||||
audit_logs 9 event_metadata json YES
|
||||
audit_logs 10 status_code integer YES
|
||||
audit_logs 11 error_message text YES
|
||||
audit_logs 12 created_at timestamp with time zone NO now()
|
||||
billing_rules 1 id character varying NO
|
||||
billing_rules 2 global_model_id character varying YES
|
||||
billing_rules 3 model_id character varying YES
|
||||
billing_rules 4 name character varying NO
|
||||
billing_rules 5 task_type character varying NO 'chat'::character varying
|
||||
billing_rules 6 expression text NO
|
||||
billing_rules 7 variables jsonb NO '{}'::jsonb
|
||||
billing_rules 8 dimension_mappings jsonb NO '{}'::jsonb
|
||||
billing_rules 9 is_enabled boolean NO true
|
||||
billing_rules 10 created_at timestamp with time zone NO now()
|
||||
billing_rules 11 updated_at timestamp with time zone NO now()
|
||||
dimension_collectors 1 id character varying NO
|
||||
dimension_collectors 2 api_format character varying NO
|
||||
dimension_collectors 3 task_type character varying NO
|
||||
dimension_collectors 4 dimension_name character varying NO
|
||||
dimension_collectors 5 source_type character varying NO
|
||||
dimension_collectors 6 source_path character varying YES
|
||||
dimension_collectors 7 value_type character varying NO 'float'::character varying
|
||||
dimension_collectors 8 transform_expression text YES
|
||||
dimension_collectors 9 default_value character varying YES
|
||||
dimension_collectors 10 priority integer NO 0
|
||||
dimension_collectors 11 is_enabled boolean NO true
|
||||
dimension_collectors 12 created_at timestamp with time zone NO now()
|
||||
dimension_collectors 13 updated_at timestamp with time zone NO now()
|
||||
gemini_file_mappings 1 id character varying NO
|
||||
gemini_file_mappings 2 file_name character varying NO
|
||||
gemini_file_mappings 3 key_id character varying NO
|
||||
gemini_file_mappings 4 user_id character varying YES
|
||||
gemini_file_mappings 5 display_name character varying YES
|
||||
gemini_file_mappings 6 mime_type character varying YES
|
||||
gemini_file_mappings 7 source_hash character varying YES
|
||||
gemini_file_mappings 8 created_at timestamp with time zone NO
|
||||
gemini_file_mappings 9 expires_at timestamp with time zone NO
|
||||
global_models 1 id character varying NO
|
||||
global_models 2 name character varying NO
|
||||
global_models 3 display_name character varying NO
|
||||
global_models 7 default_price_per_request numeric YES
|
||||
global_models 8 default_tiered_pricing json NO
|
||||
global_models 14 supported_capabilities json YES
|
||||
global_models 15 is_active boolean NO true
|
||||
global_models 16 usage_count integer NO 0
|
||||
global_models 17 created_at timestamp with time zone NO now()
|
||||
global_models 18 updated_at timestamp with time zone NO now()
|
||||
global_models 19 config jsonb YES
|
||||
ldap_configs 1 id integer NO nextval('ldap_configs_id_seq'::regclass)
|
||||
ldap_configs 2 server_url character varying NO
|
||||
ldap_configs 3 bind_dn text NO
|
||||
ldap_configs 4 bind_password_encrypted text YES
|
||||
ldap_configs 5 base_dn text NO
|
||||
ldap_configs 6 user_search_filter text NO '(uid={username})'::character varying
|
||||
ldap_configs 7 username_attr character varying NO 'uid'::character varying
|
||||
ldap_configs 8 email_attr character varying NO 'mail'::character varying
|
||||
ldap_configs 9 display_name_attr character varying NO 'cn'::character varying
|
||||
ldap_configs 10 is_enabled boolean NO false
|
||||
ldap_configs 11 is_exclusive boolean NO false
|
||||
ldap_configs 12 use_starttls boolean NO false
|
||||
ldap_configs 13 connect_timeout integer NO 10
|
||||
ldap_configs 14 created_at timestamp with time zone NO now()
|
||||
ldap_configs 15 updated_at timestamp with time zone NO now()
|
||||
management_tokens 1 id character varying NO
|
||||
management_tokens 2 user_id character varying NO
|
||||
management_tokens 3 token_hash character varying NO
|
||||
management_tokens 4 token_prefix character varying YES
|
||||
management_tokens 5 name character varying NO
|
||||
management_tokens 6 description text YES
|
||||
management_tokens 7 allowed_ips json YES
|
||||
management_tokens 8 expires_at timestamp with time zone YES
|
||||
management_tokens 9 last_used_at timestamp with time zone YES
|
||||
management_tokens 10 last_used_ip character varying YES
|
||||
management_tokens 11 usage_count integer NO 0
|
||||
management_tokens 12 is_active boolean NO true
|
||||
management_tokens 13 created_at timestamp with time zone NO now()
|
||||
management_tokens 14 updated_at timestamp with time zone NO now()
|
||||
models 1 id character varying NO
|
||||
models 2 provider_id character varying NO
|
||||
models 3 global_model_id character varying NO
|
||||
models 4 provider_model_name character varying NO
|
||||
models 5 price_per_request numeric YES
|
||||
models 6 tiered_pricing json YES
|
||||
models 7 supports_vision boolean YES
|
||||
models 8 supports_function_calling boolean YES
|
||||
models 9 supports_streaming boolean YES
|
||||
models 10 supports_extended_thinking boolean YES
|
||||
models 11 supports_image_generation boolean YES
|
||||
models 12 is_active boolean NO true
|
||||
models 13 is_available boolean YES true
|
||||
models 14 config json YES
|
||||
models 15 created_at timestamp with time zone NO now()
|
||||
models 16 updated_at timestamp with time zone NO now()
|
||||
models 17 provider_model_mappings jsonb YES
|
||||
oauth_providers 1 provider_type character varying NO
|
||||
oauth_providers 2 display_name character varying NO
|
||||
oauth_providers 3 client_id text NO
|
||||
oauth_providers 4 client_secret_encrypted text YES
|
||||
oauth_providers 5 authorization_url_override character varying YES
|
||||
oauth_providers 6 token_url_override character varying YES
|
||||
oauth_providers 7 userinfo_url_override character varying YES
|
||||
oauth_providers 8 scopes json YES
|
||||
oauth_providers 9 redirect_uri character varying NO
|
||||
oauth_providers 10 frontend_callback_url character varying NO
|
||||
oauth_providers 11 attribute_mapping json YES
|
||||
oauth_providers 12 extra_config json YES
|
||||
oauth_providers 13 is_enabled boolean NO false
|
||||
oauth_providers 14 created_at timestamp with time zone NO CURRENT_TIMESTAMP
|
||||
oauth_providers 15 updated_at timestamp with time zone NO CURRENT_TIMESTAMP
|
||||
payment_callbacks 1 id character varying NO
|
||||
payment_callbacks 2 payment_order_id character varying YES
|
||||
payment_callbacks 3 payment_method character varying NO
|
||||
payment_callbacks 4 callback_key character varying NO
|
||||
payment_callbacks 5 order_no character varying YES
|
||||
payment_callbacks 6 gateway_order_id character varying YES
|
||||
payment_callbacks 7 payload_hash character varying YES
|
||||
payment_callbacks 8 signature_valid boolean NO false
|
||||
payment_callbacks 9 status character varying NO 'received'::character varying
|
||||
payment_callbacks 10 payload jsonb YES
|
||||
payment_callbacks 11 error_message text YES
|
||||
payment_callbacks 12 created_at timestamp with time zone NO
|
||||
payment_callbacks 13 processed_at timestamp with time zone YES
|
||||
payment_orders 1 id character varying NO
|
||||
payment_orders 2 order_no character varying NO
|
||||
payment_orders 3 wallet_id character varying NO
|
||||
payment_orders 4 user_id character varying YES
|
||||
payment_orders 5 amount_usd numeric NO
|
||||
payment_orders 6 pay_amount numeric YES
|
||||
payment_orders 7 pay_currency character varying YES
|
||||
payment_orders 8 exchange_rate numeric YES
|
||||
payment_orders 9 refunded_amount_usd numeric NO '0'::numeric
|
||||
payment_orders 10 refundable_amount_usd numeric NO '0'::numeric
|
||||
payment_orders 11 payment_method character varying NO
|
||||
payment_orders 12 gateway_order_id character varying YES
|
||||
payment_orders 13 gateway_response jsonb YES
|
||||
payment_orders 14 status character varying NO 'pending'::character varying
|
||||
payment_orders 15 created_at timestamp with time zone NO
|
||||
payment_orders 16 paid_at timestamp with time zone YES
|
||||
payment_orders 17 credited_at timestamp with time zone YES
|
||||
payment_orders 18 expires_at timestamp with time zone YES
|
||||
provider_api_keys 1 id character varying NO
|
||||
provider_api_keys 3 api_key text NO
|
||||
provider_api_keys 4 name character varying NO
|
||||
provider_api_keys 5 note character varying YES
|
||||
provider_api_keys 7 internal_priority integer YES 50
|
||||
provider_api_keys 9 rpm_limit integer YES RPM限制(NULL=自适应模式)
|
||||
provider_api_keys 13 allowed_models json YES
|
||||
provider_api_keys 14 capabilities json YES
|
||||
provider_api_keys 15 learned_rpm_limit integer YES 学习到的RPM限制
|
||||
provider_api_keys 16 concurrent_429_count integer NO 0
|
||||
provider_api_keys 17 rpm_429_count integer NO 0
|
||||
provider_api_keys 18 last_429_at timestamp with time zone YES
|
||||
provider_api_keys 19 last_429_type character varying YES
|
||||
provider_api_keys 20 last_rpm_peak integer YES 触发429时的RPM峰值
|
||||
provider_api_keys 21 adjustment_history json YES
|
||||
provider_api_keys 22 utilization_samples json YES
|
||||
provider_api_keys 23 last_probe_increase_at timestamp with time zone YES
|
||||
provider_api_keys 27 cache_ttl_minutes integer NO 5
|
||||
provider_api_keys 28 max_probe_interval_minutes integer NO 32
|
||||
provider_api_keys 36 request_count integer YES 0
|
||||
provider_api_keys 37 success_count integer YES 0
|
||||
provider_api_keys 38 error_count integer YES 0
|
||||
provider_api_keys 39 total_response_time_ms integer YES 0
|
||||
provider_api_keys 40 last_used_at timestamp with time zone YES
|
||||
provider_api_keys 41 last_error_at timestamp with time zone YES
|
||||
provider_api_keys 42 last_error_msg text YES
|
||||
provider_api_keys 43 is_active boolean NO true
|
||||
provider_api_keys 44 expires_at timestamp with time zone YES
|
||||
provider_api_keys 45 created_at timestamp with time zone NO now()
|
||||
provider_api_keys 46 updated_at timestamp with time zone NO now()
|
||||
provider_api_keys 64 provider_id character varying NO
|
||||
provider_api_keys 65 api_formats json NO '[]'::json
|
||||
provider_api_keys 66 rate_multipliers json YES
|
||||
provider_api_keys 67 health_by_format jsonb YES 按API格式存储的健康度数据
|
||||
provider_api_keys 68 circuit_breaker_by_format jsonb YES 按API格式存储的熔断器状态
|
||||
provider_api_keys 73 auto_fetch_models boolean NO false
|
||||
provider_api_keys 74 last_models_fetch_at timestamp with time zone YES
|
||||
provider_api_keys 75 last_models_fetch_error text YES
|
||||
provider_api_keys 76 locked_models json YES
|
||||
provider_api_keys 77 global_priority_by_format json YES
|
||||
provider_api_keys 80 model_include_patterns json YES
|
||||
provider_api_keys 81 model_exclude_patterns json YES
|
||||
provider_api_keys 82 auth_type character varying NO 'api_key'::character varying
|
||||
provider_api_keys 83 auth_config text YES
|
||||
provider_api_keys 84 upstream_metadata jsonb YES
|
||||
provider_api_keys 85 oauth_invalid_at timestamp with time zone YES
|
||||
provider_api_keys 86 oauth_invalid_reason character varying YES
|
||||
provider_api_keys 87 proxy json YES Key 级别代理配置(覆盖 Provider 级别代理),如 {node_id, enabled}
|
||||
provider_api_keys 88 fingerprint json YES
|
||||
provider_api_keys 89 total_tokens bigint NO
|
||||
provider_api_keys 90 total_cost_usd numeric NO
|
||||
provider_api_keys 91 status_snapshot json YES
|
||||
provider_endpoints 1 id character varying NO
|
||||
provider_endpoints 2 provider_id character varying NO
|
||||
provider_endpoints 3 api_format character varying NO
|
||||
provider_endpoints 4 base_url character varying NO
|
||||
provider_endpoints 7 max_retries integer YES 3
|
||||
provider_endpoints 10 is_active boolean NO true
|
||||
provider_endpoints 11 custom_path character varying YES
|
||||
provider_endpoints 12 config json YES
|
||||
provider_endpoints 13 created_at timestamp with time zone NO now()
|
||||
provider_endpoints 14 updated_at timestamp with time zone NO now()
|
||||
provider_endpoints 15 proxy jsonb YES
|
||||
provider_endpoints 22 header_rules json YES
|
||||
provider_endpoints 23 format_acceptance_config json YES
|
||||
provider_endpoints 24 api_family character varying YES
|
||||
provider_endpoints 25 endpoint_kind character varying YES
|
||||
provider_endpoints 27 body_rules json YES
|
||||
provider_endpoints 28 health_score double precision NO 1.0
|
||||
provider_usage_tracking 1 id character varying NO
|
||||
provider_usage_tracking 2 provider_id character varying NO
|
||||
provider_usage_tracking 3 window_start timestamp with time zone NO
|
||||
provider_usage_tracking 4 window_end timestamp with time zone NO
|
||||
provider_usage_tracking 5 total_requests integer YES 0
|
||||
provider_usage_tracking 6 successful_requests integer YES 0
|
||||
provider_usage_tracking 7 failed_requests integer YES 0
|
||||
provider_usage_tracking 8 avg_response_time_ms double precision YES '0'::double precision
|
||||
provider_usage_tracking 9 total_response_time_ms double precision YES '0'::double precision
|
||||
provider_usage_tracking 10 total_cost_usd double precision YES '0'::double precision
|
||||
provider_usage_tracking 11 created_at timestamp with time zone NO now()
|
||||
provider_usage_tracking 12 updated_at timestamp with time zone NO now()
|
||||
providers 1 id character varying NO
|
||||
providers 3 name character varying NO
|
||||
providers 4 description text YES
|
||||
providers 5 website character varying YES
|
||||
providers 6 billing_type USER-DEFINED NO 'pay_as_you_go'::providerbillingtype
|
||||
providers 7 monthly_quota_usd numeric YES
|
||||
providers 8 monthly_used_usd numeric YES '0'::double precision
|
||||
providers 9 quota_reset_day integer YES 30
|
||||
providers 10 quota_last_reset_at timestamp with time zone YES
|
||||
providers 11 quota_expires_at timestamp with time zone YES
|
||||
providers 15 provider_priority integer YES 100
|
||||
providers 16 is_active boolean NO true
|
||||
providers 18 concurrent_limit integer YES
|
||||
providers 19 config json YES
|
||||
providers 20 created_at timestamp with time zone NO now()
|
||||
providers 21 updated_at timestamp with time zone NO now()
|
||||
providers 34 max_retries integer YES 最大重试次数
|
||||
providers 35 proxy jsonb YES 代理配置
|
||||
providers 36 stream_first_byte_timeout double precision YES
|
||||
providers 37 request_timeout double precision YES
|
||||
providers 38 keep_priority_on_conversion boolean NO false
|
||||
providers 39 enable_format_conversion boolean NO true
|
||||
providers 40 provider_type character varying NO 'custom'::character varying
|
||||
proxy_node_events 1 id bigint NO nextval('proxy_node_events_id_seq'::regclass)
|
||||
proxy_node_events 2 node_id character varying NO
|
||||
proxy_node_events 3 event_type character varying NO 事件类型: connected, disconnected, error
|
||||
proxy_node_events 4 detail character varying YES 事件详情(如断开原因)
|
||||
proxy_node_events 5 created_at timestamp with time zone NO
|
||||
proxy_nodes 1 id character varying NO
|
||||
proxy_nodes 2 name character varying NO
|
||||
proxy_nodes 3 ip character varying NO
|
||||
proxy_nodes 4 port integer NO
|
||||
proxy_nodes 5 region character varying YES
|
||||
proxy_nodes 6 status USER-DEFINED NO 'online'::proxynodestatus
|
||||
proxy_nodes 7 registered_by character varying YES
|
||||
proxy_nodes 8 last_heartbeat_at timestamp with time zone YES
|
||||
proxy_nodes 9 heartbeat_interval integer NO 30
|
||||
proxy_nodes 10 active_connections integer NO 0
|
||||
proxy_nodes 11 total_requests bigint NO 0
|
||||
proxy_nodes 12 avg_latency_ms double precision YES
|
||||
proxy_nodes 13 is_manual boolean NO false 是否为手动添加的代理节点
|
||||
proxy_nodes 14 proxy_url character varying YES 手动节点的完整代理 URL
|
||||
proxy_nodes 15 proxy_username character varying YES 手动节点的代理用户名
|
||||
proxy_nodes 16 proxy_password character varying YES 手动节点的代理密码
|
||||
proxy_nodes 17 created_at timestamp with time zone NO CURRENT_TIMESTAMP
|
||||
proxy_nodes 18 updated_at timestamp with time zone NO CURRENT_TIMESTAMP
|
||||
proxy_nodes 19 remote_config json YES 管理端下发的远程配置 (allowed_ports, log_level, heartbeat_interval, timestamp_tolerance)
|
||||
proxy_nodes 20 config_version integer NO 0 远程配置版本号,每次更新 +1
|
||||
proxy_nodes 23 hardware_info json YES 硬件信息 (cpu_cores, total_memory_mb, os_info, fd_limit, ...)
|
||||
proxy_nodes 24 estimated_max_concurrency integer YES 基于硬件估算的最大并发连接数
|
||||
proxy_nodes 28 tunnel_mode boolean NO false 是否使用 WebSocket 隧道模式
|
||||
proxy_nodes 29 tunnel_connected boolean NO false 隧道是否已连接
|
||||
proxy_nodes 30 tunnel_connected_at timestamp with time zone YES 隧道最近一次建立时间
|
||||
proxy_nodes 31 failed_requests bigint NO '0'::bigint 累计失败请求数
|
||||
proxy_nodes 32 dns_failures bigint NO '0'::bigint 累计 DNS 失败数
|
||||
proxy_nodes 33 stream_errors bigint NO '0'::bigint 累计流错误数
|
||||
proxy_nodes 34 proxy_metadata json YES aether-proxy 上报元数据(版本等)
|
||||
refund_requests 1 id character varying NO
|
||||
refund_requests 2 refund_no character varying NO
|
||||
refund_requests 3 wallet_id character varying NO
|
||||
refund_requests 4 user_id character varying YES
|
||||
refund_requests 5 payment_order_id character varying YES
|
||||
refund_requests 6 source_type character varying NO
|
||||
refund_requests 7 source_id character varying YES
|
||||
refund_requests 8 refund_mode character varying NO
|
||||
refund_requests 9 amount_usd numeric NO
|
||||
refund_requests 10 status character varying NO 'pending_approval'::character varying
|
||||
refund_requests 11 reason text YES
|
||||
refund_requests 12 requested_by character varying YES
|
||||
refund_requests 13 approved_by character varying YES
|
||||
refund_requests 14 processed_by character varying YES
|
||||
refund_requests 15 gateway_refund_id character varying YES
|
||||
refund_requests 16 payout_method character varying YES
|
||||
refund_requests 17 payout_reference character varying YES
|
||||
refund_requests 18 payout_proof jsonb YES
|
||||
refund_requests 19 failure_reason text YES
|
||||
refund_requests 20 idempotency_key character varying YES
|
||||
refund_requests 21 created_at timestamp with time zone NO
|
||||
refund_requests 22 updated_at timestamp with time zone NO
|
||||
refund_requests 23 processed_at timestamp with time zone YES
|
||||
refund_requests 24 completed_at timestamp with time zone YES
|
||||
request_candidates 1 id character varying NO
|
||||
request_candidates 2 request_id character varying NO
|
||||
request_candidates 3 user_id character varying YES
|
||||
request_candidates 4 api_key_id character varying YES
|
||||
request_candidates 5 candidate_index integer NO
|
||||
request_candidates 6 retry_index integer NO 0
|
||||
request_candidates 7 provider_id character varying YES
|
||||
request_candidates 8 endpoint_id character varying YES
|
||||
request_candidates 9 key_id character varying YES
|
||||
request_candidates 10 status character varying NO
|
||||
request_candidates 11 skip_reason text YES
|
||||
request_candidates 12 is_cached boolean YES false
|
||||
request_candidates 13 status_code integer YES
|
||||
request_candidates 14 error_type character varying YES
|
||||
request_candidates 15 error_message text YES
|
||||
request_candidates 16 latency_ms integer YES
|
||||
request_candidates 17 concurrent_requests integer YES
|
||||
request_candidates 18 extra_data json YES
|
||||
request_candidates 19 required_capabilities json YES
|
||||
request_candidates 20 created_at timestamp with time zone NO now()
|
||||
request_candidates 21 started_at timestamp with time zone YES
|
||||
request_candidates 22 finished_at timestamp with time zone YES
|
||||
request_candidates 23 username character varying YES 用户名快照
|
||||
request_candidates 24 api_key_name character varying YES API Key 名称快照
|
||||
stats_daily 1 id character varying NO
|
||||
stats_daily 2 date timestamp with time zone NO
|
||||
stats_daily 3 total_requests integer NO 0
|
||||
stats_daily 4 success_requests integer NO 0
|
||||
stats_daily 5 error_requests integer NO 0
|
||||
stats_daily 6 input_tokens bigint NO '0'::bigint
|
||||
stats_daily 7 output_tokens bigint NO '0'::bigint
|
||||
stats_daily 8 cache_creation_tokens bigint NO '0'::bigint
|
||||
stats_daily 9 cache_read_tokens bigint NO '0'::bigint
|
||||
stats_daily 10 total_cost numeric NO '0'::double precision
|
||||
stats_daily 11 actual_total_cost numeric NO '0'::double precision
|
||||
stats_daily 12 input_cost numeric NO '0'::double precision
|
||||
stats_daily 13 output_cost numeric NO '0'::double precision
|
||||
stats_daily 14 cache_creation_cost numeric NO '0'::double precision
|
||||
stats_daily 15 cache_read_cost numeric NO '0'::double precision
|
||||
stats_daily 16 avg_response_time_ms double precision NO '0'::double precision
|
||||
stats_daily 17 fallback_count integer NO 0
|
||||
stats_daily 18 unique_models integer NO 0
|
||||
stats_daily 19 unique_providers integer NO 0
|
||||
stats_daily 20 created_at timestamp with time zone NO now()
|
||||
stats_daily 21 updated_at timestamp with time zone NO now()
|
||||
stats_daily 22 is_complete boolean NO false
|
||||
stats_daily 23 aggregated_at timestamp with time zone YES
|
||||
stats_daily 24 p50_response_time_ms integer YES
|
||||
stats_daily 25 p90_response_time_ms integer YES
|
||||
stats_daily 26 p99_response_time_ms integer YES
|
||||
stats_daily 27 p50_first_byte_time_ms integer YES
|
||||
stats_daily 28 p90_first_byte_time_ms integer YES
|
||||
stats_daily 29 p99_first_byte_time_ms integer YES
|
||||
stats_daily_api_key 1 id character varying NO
|
||||
stats_daily_api_key 2 api_key_id character varying YES
|
||||
stats_daily_api_key 3 date timestamp with time zone NO
|
||||
stats_daily_api_key 4 total_requests integer NO 0
|
||||
stats_daily_api_key 5 success_requests integer NO 0
|
||||
stats_daily_api_key 6 error_requests integer NO 0
|
||||
stats_daily_api_key 7 input_tokens bigint NO '0'::bigint
|
||||
stats_daily_api_key 8 output_tokens bigint NO '0'::bigint
|
||||
stats_daily_api_key 9 cache_creation_tokens bigint NO '0'::bigint
|
||||
stats_daily_api_key 10 cache_read_tokens bigint NO '0'::bigint
|
||||
stats_daily_api_key 11 total_cost numeric NO '0'::double precision
|
||||
stats_daily_api_key 12 created_at timestamp with time zone NO CURRENT_TIMESTAMP
|
||||
stats_daily_api_key 13 updated_at timestamp with time zone NO CURRENT_TIMESTAMP
|
||||
stats_daily_api_key 14 api_key_name character varying YES API Key 名称快照(删除 Key 后仍可追溯)
|
||||
stats_daily_error 1 id character varying NO
|
||||
stats_daily_error 2 date timestamp with time zone NO
|
||||
stats_daily_error 3 error_category character varying NO
|
||||
stats_daily_error 4 provider_name character varying YES
|
||||
stats_daily_error 5 model character varying YES
|
||||
stats_daily_error 6 count integer NO 0
|
||||
stats_daily_error 7 created_at timestamp with time zone NO CURRENT_TIMESTAMP
|
||||
stats_daily_error 8 updated_at timestamp with time zone NO CURRENT_TIMESTAMP
|
||||
stats_daily_model 1 id character varying NO
|
||||
stats_daily_model 2 date timestamp with time zone NO
|
||||
stats_daily_model 3 model character varying NO
|
||||
stats_daily_model 4 total_requests integer NO
|
||||
stats_daily_model 5 input_tokens bigint NO
|
||||
stats_daily_model 6 output_tokens bigint NO
|
||||
stats_daily_model 7 cache_creation_tokens bigint NO
|
||||
stats_daily_model 8 cache_read_tokens bigint NO
|
||||
stats_daily_model 9 total_cost numeric NO
|
||||
stats_daily_model 10 avg_response_time_ms double precision NO
|
||||
stats_daily_model 11 created_at timestamp with time zone NO now()
|
||||
stats_daily_model 12 updated_at timestamp with time zone NO now()
|
||||
stats_daily_provider 1 id character varying NO
|
||||
stats_daily_provider 2 date timestamp with time zone NO
|
||||
stats_daily_provider 3 provider_name character varying NO
|
||||
stats_daily_provider 4 total_requests integer NO
|
||||
stats_daily_provider 5 input_tokens bigint NO
|
||||
stats_daily_provider 6 output_tokens bigint NO
|
||||
stats_daily_provider 7 cache_creation_tokens bigint NO
|
||||
stats_daily_provider 8 cache_read_tokens bigint NO
|
||||
stats_daily_provider 9 total_cost numeric NO
|
||||
stats_daily_provider 10 created_at timestamp with time zone NO
|
||||
stats_daily_provider 11 updated_at timestamp with time zone NO
|
||||
stats_hourly 1 id character varying NO
|
||||
stats_hourly 2 hour_utc timestamp with time zone NO
|
||||
stats_hourly 3 total_requests integer NO
|
||||
stats_hourly 4 success_requests integer NO
|
||||
stats_hourly 5 error_requests integer NO
|
||||
stats_hourly 6 input_tokens bigint NO
|
||||
stats_hourly 7 output_tokens bigint NO
|
||||
stats_hourly 8 cache_creation_tokens bigint NO
|
||||
stats_hourly 9 cache_read_tokens bigint NO
|
||||
stats_hourly 10 total_cost numeric NO
|
||||
stats_hourly 11 actual_total_cost numeric NO
|
||||
stats_hourly 12 avg_response_time_ms double precision NO
|
||||
stats_hourly 13 is_complete boolean NO
|
||||
stats_hourly 14 aggregated_at timestamp with time zone YES
|
||||
stats_hourly 15 created_at timestamp with time zone NO
|
||||
stats_hourly 16 updated_at timestamp with time zone NO
|
||||
stats_hourly_model 1 id character varying NO
|
||||
stats_hourly_model 2 hour_utc timestamp with time zone NO
|
||||
stats_hourly_model 3 model character varying NO
|
||||
stats_hourly_model 4 total_requests integer NO
|
||||
stats_hourly_model 5 input_tokens bigint NO
|
||||
stats_hourly_model 6 output_tokens bigint NO
|
||||
stats_hourly_model 7 total_cost numeric NO
|
||||
stats_hourly_model 8 avg_response_time_ms double precision NO
|
||||
stats_hourly_model 9 created_at timestamp with time zone NO
|
||||
stats_hourly_model 10 updated_at timestamp with time zone NO
|
||||
stats_hourly_provider 1 id character varying NO
|
||||
stats_hourly_provider 2 hour_utc timestamp with time zone NO
|
||||
stats_hourly_provider 3 provider_name character varying NO
|
||||
stats_hourly_provider 4 total_requests integer NO
|
||||
stats_hourly_provider 5 input_tokens bigint NO
|
||||
stats_hourly_provider 6 output_tokens bigint NO
|
||||
stats_hourly_provider 7 total_cost numeric NO
|
||||
stats_hourly_provider 8 created_at timestamp with time zone NO
|
||||
stats_hourly_provider 9 updated_at timestamp with time zone NO
|
||||
stats_hourly_user 1 id character varying NO
|
||||
stats_hourly_user 2 hour_utc timestamp with time zone NO
|
||||
stats_hourly_user 3 user_id character varying NO
|
||||
stats_hourly_user 4 total_requests integer NO
|
||||
stats_hourly_user 5 success_requests integer NO
|
||||
stats_hourly_user 6 error_requests integer NO
|
||||
stats_hourly_user 7 input_tokens bigint NO
|
||||
stats_hourly_user 8 output_tokens bigint NO
|
||||
stats_hourly_user 9 total_cost numeric NO
|
||||
stats_hourly_user 10 created_at timestamp with time zone NO
|
||||
stats_hourly_user 11 updated_at timestamp with time zone NO
|
||||
stats_summary 1 id character varying NO
|
||||
stats_summary 2 cutoff_date timestamp with time zone NO
|
||||
stats_summary 3 all_time_requests integer NO 0
|
||||
stats_summary 4 all_time_success_requests integer NO 0
|
||||
stats_summary 5 all_time_error_requests integer NO 0
|
||||
stats_summary 6 all_time_input_tokens bigint NO '0'::bigint
|
||||
stats_summary 7 all_time_output_tokens bigint NO '0'::bigint
|
||||
stats_summary 8 all_time_cache_creation_tokens bigint NO '0'::bigint
|
||||
stats_summary 9 all_time_cache_read_tokens bigint NO '0'::bigint
|
||||
stats_summary 10 all_time_cost numeric NO '0'::double precision
|
||||
stats_summary 11 all_time_actual_cost numeric NO '0'::double precision
|
||||
stats_summary 12 total_users integer NO 0
|
||||
stats_summary 13 active_users integer NO 0
|
||||
stats_summary 14 total_api_keys integer NO 0
|
||||
stats_summary 15 active_api_keys integer NO 0
|
||||
stats_summary 16 created_at timestamp with time zone NO now()
|
||||
stats_summary 17 updated_at timestamp with time zone NO now()
|
||||
stats_user_daily 1 id character varying NO
|
||||
stats_user_daily 2 user_id character varying YES
|
||||
stats_user_daily 3 date timestamp with time zone NO
|
||||
stats_user_daily 4 total_requests integer NO 0
|
||||
stats_user_daily 5 success_requests integer NO 0
|
||||
stats_user_daily 6 error_requests integer NO 0
|
||||
stats_user_daily 7 input_tokens bigint NO '0'::bigint
|
||||
stats_user_daily 8 output_tokens bigint NO '0'::bigint
|
||||
stats_user_daily 9 cache_creation_tokens bigint NO '0'::bigint
|
||||
stats_user_daily 10 cache_read_tokens bigint NO '0'::bigint
|
||||
stats_user_daily 11 total_cost numeric NO '0'::double precision
|
||||
stats_user_daily 12 created_at timestamp with time zone NO now()
|
||||
stats_user_daily 13 updated_at timestamp with time zone NO now()
|
||||
stats_user_daily 14 username character varying YES 用户名快照(删除用户后仍可追溯)
|
||||
system_configs 1 id character varying NO
|
||||
system_configs 2 key character varying NO
|
||||
system_configs 3 value json NO
|
||||
system_configs 4 description text YES
|
||||
system_configs 5 created_at timestamp with time zone NO now()
|
||||
system_configs 6 updated_at timestamp with time zone NO now()
|
||||
usage 1 id character varying NO
|
||||
usage 2 user_id character varying YES
|
||||
usage 3 api_key_id character varying YES
|
||||
usage 4 request_id character varying NO
|
||||
usage 5 provider_name character varying NO
|
||||
usage 6 model character varying NO
|
||||
usage 7 target_model character varying YES
|
||||
usage 8 provider_id character varying YES
|
||||
usage 9 provider_endpoint_id character varying YES
|
||||
usage 10 provider_api_key_id character varying YES
|
||||
usage 11 input_tokens integer YES 0
|
||||
usage 12 output_tokens integer YES 0
|
||||
usage 13 input_output_total_tokens integer YES 0
|
||||
usage 14 cache_creation_input_tokens integer YES 0
|
||||
usage 15 cache_read_input_tokens integer YES 0
|
||||
usage 16 input_cost_usd numeric YES '0'::double precision
|
||||
usage 17 output_cost_usd numeric YES '0'::double precision
|
||||
usage 18 cache_cost_usd numeric YES '0'::double precision
|
||||
usage 19 cache_creation_cost_usd numeric YES '0'::double precision
|
||||
usage 20 cache_read_cost_usd numeric YES '0'::double precision
|
||||
usage 21 request_cost_usd numeric YES '0'::double precision
|
||||
usage 22 total_cost_usd numeric YES '0'::double precision
|
||||
usage 23 actual_input_cost_usd numeric YES '0'::double precision
|
||||
usage 24 actual_output_cost_usd numeric YES '0'::double precision
|
||||
usage 25 actual_cache_creation_cost_usd numeric YES '0'::double precision
|
||||
usage 26 actual_cache_read_cost_usd numeric YES '0'::double precision
|
||||
usage 27 actual_request_cost_usd numeric YES '0'::double precision
|
||||
usage 28 actual_total_cost_usd numeric YES '0'::double precision
|
||||
usage 29 rate_multiplier numeric YES '1'::double precision
|
||||
usage 30 input_price_per_1m numeric YES
|
||||
usage 31 output_price_per_1m numeric YES
|
||||
usage 32 cache_creation_price_per_1m numeric YES
|
||||
usage 33 cache_read_price_per_1m numeric YES
|
||||
usage 34 price_per_request numeric YES
|
||||
usage 35 request_type character varying YES
|
||||
usage 36 api_format character varying YES
|
||||
usage 37 is_stream boolean YES false
|
||||
usage 38 status_code integer YES
|
||||
usage 39 error_message text YES
|
||||
usage 40 response_time_ms integer YES
|
||||
usage 41 status character varying NO 'completed'::character varying
|
||||
usage 42 request_headers json YES
|
||||
usage 43 request_body json YES
|
||||
usage 44 provider_request_headers json YES
|
||||
usage 45 response_headers json YES
|
||||
usage 46 response_body json YES
|
||||
usage 47 request_body_compressed bytea YES
|
||||
usage 48 response_body_compressed bytea YES
|
||||
usage 49 request_metadata json YES
|
||||
usage 50 created_at timestamp with time zone NO now()
|
||||
usage 51 first_byte_time_ms integer YES
|
||||
usage 54 client_response_headers json YES
|
||||
usage 59 endpoint_api_format character varying YES
|
||||
usage 60 has_format_conversion boolean YES false
|
||||
usage 63 billing_status character varying NO 'pending'::character varying
|
||||
usage 64 finalized_at timestamp with time zone YES
|
||||
usage 65 error_category character varying YES
|
||||
usage 66 provider_request_body json YES
|
||||
usage 67 provider_request_body_compressed bytea YES
|
||||
usage 68 client_response_body json YES
|
||||
usage 69 client_response_body_compressed bytea YES
|
||||
usage 70 api_family character varying YES
|
||||
usage 71 endpoint_kind character varying YES
|
||||
usage 72 provider_api_family character varying YES
|
||||
usage 73 provider_endpoint_kind character varying YES
|
||||
usage 74 cache_creation_input_tokens_5m integer NO 0 5min TTL cache creation input tokens
|
||||
usage 75 cache_creation_input_tokens_1h integer NO 0 1h TTL cache creation input tokens
|
||||
usage 76 wallet_id character varying YES
|
||||
usage 77 wallet_balance_before numeric YES
|
||||
usage 78 wallet_balance_after numeric YES
|
||||
usage 79 wallet_recharge_balance_before numeric YES
|
||||
usage 80 wallet_recharge_balance_after numeric YES
|
||||
usage 81 wallet_gift_balance_before numeric YES
|
||||
usage 82 wallet_gift_balance_after numeric YES
|
||||
usage 83 username character varying YES 用户名快照
|
||||
usage 84 api_key_name character varying YES API Key 名称快照
|
||||
usage 85 input_context_tokens integer NO 0
|
||||
usage 86 total_tokens integer NO 0
|
||||
usage 87 cache_creation_cost_usd_5m numeric NO '0'::numeric
|
||||
usage 88 cache_creation_cost_usd_1h numeric NO '0'::numeric
|
||||
usage 89 actual_cache_creation_cost_usd_5m numeric NO '0'::numeric
|
||||
usage 90 actual_cache_creation_cost_usd_1h numeric NO '0'::numeric
|
||||
usage 91 actual_cache_cost_usd numeric NO '0'::numeric
|
||||
usage 92 cache_creation_price_per_1m_5m numeric YES
|
||||
usage 93 cache_creation_price_per_1m_1h numeric YES
|
||||
user_model_usage_counts 1 id character varying NO
|
||||
user_model_usage_counts 2 user_id character varying NO
|
||||
user_model_usage_counts 3 model character varying NO
|
||||
user_model_usage_counts 4 usage_count integer NO 0
|
||||
user_model_usage_counts 5 created_at timestamp with time zone NO now()
|
||||
user_model_usage_counts 6 updated_at timestamp with time zone NO now()
|
||||
user_oauth_links 1 id character varying NO
|
||||
user_oauth_links 2 user_id character varying NO
|
||||
user_oauth_links 3 provider_type character varying NO
|
||||
user_oauth_links 4 provider_user_id character varying NO
|
||||
user_oauth_links 5 provider_username character varying YES
|
||||
user_oauth_links 6 provider_email character varying YES
|
||||
user_oauth_links 7 extra_data json YES
|
||||
user_oauth_links 8 linked_at timestamp with time zone NO CURRENT_TIMESTAMP
|
||||
user_oauth_links 9 last_login_at timestamp with time zone YES
|
||||
user_preferences 1 id character varying NO
|
||||
user_preferences 2 user_id character varying NO
|
||||
user_preferences 3 avatar_url character varying YES
|
||||
user_preferences 4 bio text YES
|
||||
user_preferences 5 default_provider_id character varying YES
|
||||
user_preferences 6 theme character varying YES 'light'::character varying
|
||||
user_preferences 7 language character varying YES 'zh-CN'::character varying
|
||||
user_preferences 8 timezone character varying YES 'Asia/Shanghai'::character varying
|
||||
user_preferences 9 email_notifications boolean YES true
|
||||
user_preferences 10 usage_alerts boolean YES true
|
||||
user_preferences 11 announcement_notifications boolean YES true
|
||||
user_preferences 12 created_at timestamp with time zone NO now()
|
||||
user_preferences 13 updated_at timestamp with time zone NO now()
|
||||
user_sessions 1 id character varying NO
|
||||
user_sessions 2 user_id character varying NO
|
||||
user_sessions 3 client_device_id character varying NO
|
||||
user_sessions 4 device_label character varying YES
|
||||
user_sessions 5 device_type character varying NO 'unknown'::character varying
|
||||
user_sessions 6 browser_name character varying YES
|
||||
user_sessions 7 browser_version character varying YES
|
||||
user_sessions 8 os_name character varying YES
|
||||
user_sessions 9 os_version character varying YES
|
||||
user_sessions 10 device_model character varying YES
|
||||
user_sessions 11 ip_address character varying YES
|
||||
user_sessions 12 user_agent character varying YES
|
||||
user_sessions 13 client_hints json YES
|
||||
user_sessions 14 refresh_token_hash character varying NO
|
||||
user_sessions 15 prev_refresh_token_hash character varying YES
|
||||
user_sessions 16 rotated_at timestamp with time zone YES
|
||||
user_sessions 17 last_seen_at timestamp with time zone NO now()
|
||||
user_sessions 18 expires_at timestamp with time zone NO
|
||||
user_sessions 19 revoked_at timestamp with time zone YES
|
||||
user_sessions 20 revoke_reason character varying YES
|
||||
user_sessions 21 created_at timestamp with time zone NO now()
|
||||
user_sessions 22 updated_at timestamp with time zone NO now()
|
||||
users 1 id character varying NO
|
||||
users 2 email character varying YES
|
||||
users 3 username character varying NO
|
||||
users 4 password_hash character varying YES
|
||||
users 5 role USER-DEFINED NO 'user'::userrole
|
||||
users 6 allowed_providers json YES
|
||||
users 7 allowed_api_formats json YES
|
||||
users 8 allowed_models json YES
|
||||
users 9 model_capability_settings json YES
|
||||
users 13 is_active boolean NO true
|
||||
users 14 is_deleted boolean NO false
|
||||
users 15 created_at timestamp with time zone NO now()
|
||||
users 16 updated_at timestamp with time zone NO now()
|
||||
users 17 last_login_at timestamp with time zone YES
|
||||
users 18 auth_source USER-DEFINED NO 'local'::authsource
|
||||
users 19 ldap_dn character varying YES
|
||||
users 20 ldap_username character varying YES
|
||||
users 21 email_verified boolean NO
|
||||
users 22 rate_limit integer YES
|
||||
video_tasks 1 id character varying NO
|
||||
video_tasks 2 external_task_id character varying YES
|
||||
video_tasks 3 user_id character varying YES
|
||||
video_tasks 4 api_key_id character varying YES
|
||||
video_tasks 5 provider_id character varying YES
|
||||
video_tasks 6 endpoint_id character varying YES
|
||||
video_tasks 7 key_id character varying YES
|
||||
video_tasks 8 client_api_format character varying NO
|
||||
video_tasks 9 provider_api_format character varying NO
|
||||
video_tasks 10 format_converted boolean YES false
|
||||
video_tasks 11 model character varying NO
|
||||
video_tasks 12 prompt text NO
|
||||
video_tasks 13 original_request_body json YES
|
||||
video_tasks 14 converted_request_body json YES
|
||||
video_tasks 15 duration_seconds integer YES 4
|
||||
video_tasks 16 resolution character varying YES '720p'::character varying
|
||||
video_tasks 17 aspect_ratio character varying YES '16:9'::character varying
|
||||
video_tasks 18 size character varying YES
|
||||
video_tasks 19 status character varying YES 'pending'::character varying
|
||||
video_tasks 20 progress_percent integer YES 0
|
||||
video_tasks 21 progress_message character varying YES
|
||||
video_tasks 22 video_url character varying YES
|
||||
video_tasks 23 video_urls json YES
|
||||
video_tasks 24 thumbnail_url character varying YES
|
||||
video_tasks 25 video_size_bytes bigint YES
|
||||
video_tasks 26 video_expires_at timestamp with time zone YES
|
||||
video_tasks 27 stored_video_path character varying YES
|
||||
video_tasks 28 storage_provider character varying YES
|
||||
video_tasks 29 error_code character varying YES
|
||||
video_tasks 30 error_message text YES
|
||||
video_tasks 31 retry_count integer YES 0
|
||||
video_tasks 32 max_retries integer YES 3
|
||||
video_tasks 33 poll_interval_seconds integer YES 10
|
||||
video_tasks 34 next_poll_at timestamp with time zone YES
|
||||
video_tasks 35 poll_count integer YES 0
|
||||
video_tasks 36 max_poll_count integer YES 360
|
||||
video_tasks 37 remixed_from_task_id character varying YES
|
||||
video_tasks 38 webhook_url character varying YES
|
||||
video_tasks 39 webhook_sent boolean YES false
|
||||
video_tasks 40 webhook_sent_at timestamp with time zone YES
|
||||
video_tasks 41 created_at timestamp with time zone YES CURRENT_TIMESTAMP
|
||||
video_tasks 42 submitted_at timestamp with time zone YES
|
||||
video_tasks 43 completed_at timestamp with time zone YES
|
||||
video_tasks 44 updated_at timestamp with time zone YES CURRENT_TIMESTAMP
|
||||
video_tasks 46 request_metadata json YES
|
||||
video_tasks 49 request_id character varying NO
|
||||
video_tasks 50 short_id character varying NO
|
||||
video_tasks 52 video_duration_seconds double precision YES
|
||||
video_tasks 53 username character varying YES 用户名快照
|
||||
video_tasks 54 api_key_name character varying YES API Key 名称快照
|
||||
wallet_daily_usage_ledgers 1 id character varying NO
|
||||
wallet_daily_usage_ledgers 2 wallet_id character varying NO
|
||||
wallet_daily_usage_ledgers 3 billing_date date NO
|
||||
wallet_daily_usage_ledgers 4 billing_timezone character varying NO
|
||||
wallet_daily_usage_ledgers 5 total_cost_usd numeric NO '0'::numeric
|
||||
wallet_daily_usage_ledgers 6 total_requests integer NO 0
|
||||
wallet_daily_usage_ledgers 7 input_tokens bigint NO '0'::bigint
|
||||
wallet_daily_usage_ledgers 8 output_tokens bigint NO '0'::bigint
|
||||
wallet_daily_usage_ledgers 9 cache_creation_tokens bigint NO '0'::bigint
|
||||
wallet_daily_usage_ledgers 10 cache_read_tokens bigint NO '0'::bigint
|
||||
wallet_daily_usage_ledgers 11 first_finalized_at timestamp with time zone YES
|
||||
wallet_daily_usage_ledgers 12 last_finalized_at timestamp with time zone YES
|
||||
wallet_daily_usage_ledgers 13 aggregated_at timestamp with time zone NO
|
||||
wallet_daily_usage_ledgers 14 created_at timestamp with time zone NO
|
||||
wallet_daily_usage_ledgers 15 updated_at timestamp with time zone NO
|
||||
wallet_transactions 1 id character varying NO
|
||||
wallet_transactions 2 wallet_id character varying NO
|
||||
wallet_transactions 3 category character varying NO
|
||||
wallet_transactions 4 reason_code character varying NO
|
||||
wallet_transactions 5 amount numeric NO
|
||||
wallet_transactions 6 balance_before numeric NO
|
||||
wallet_transactions 7 balance_after numeric NO
|
||||
wallet_transactions 8 recharge_balance_before numeric NO
|
||||
wallet_transactions 9 recharge_balance_after numeric NO
|
||||
wallet_transactions 10 gift_balance_before numeric NO
|
||||
wallet_transactions 11 gift_balance_after numeric NO
|
||||
wallet_transactions 12 link_type character varying YES
|
||||
wallet_transactions 13 link_id character varying YES
|
||||
wallet_transactions 14 operator_id character varying YES
|
||||
wallet_transactions 15 description text YES
|
||||
wallet_transactions 16 created_at timestamp with time zone NO
|
||||
wallets 1 id character varying NO
|
||||
wallets 2 user_id character varying YES
|
||||
wallets 3 api_key_id character varying YES
|
||||
wallets 4 balance numeric NO '0'::numeric
|
||||
wallets 5 gift_balance numeric NO '0'::numeric
|
||||
wallets 6 limit_mode character varying NO 'finite'::character varying
|
||||
wallets 7 currency character varying NO 'USD'::character varying
|
||||
wallets 8 status character varying NO 'active'::character varying
|
||||
wallets 9 total_recharged numeric NO '0'::numeric
|
||||
wallets 10 total_consumed numeric NO '0'::numeric
|
||||
wallets 11 total_refunded numeric NO '0'::numeric
|
||||
wallets 12 total_adjusted numeric NO '0'::numeric
|
||||
wallets 14 created_at timestamp with time zone NO
|
||||
wallets 15 updated_at timestamp with time zone NO
|
||||
|
49
crates/aether-data/schema/current-public-tables.tsv
Normal file
49
crates/aether-data/schema/current-public-tables.tsv
Normal file
@@ -0,0 +1,49 @@
|
||||
table_name column_count
|
||||
_orphan_api_keys_backup 38
|
||||
_sqlx_migrations 6
|
||||
alembic_version 1
|
||||
announcement_reads 4
|
||||
announcements 12
|
||||
api_key_provider_mappings 8
|
||||
api_keys 21
|
||||
audit_logs 12
|
||||
billing_rules 11
|
||||
dimension_collectors 13
|
||||
gemini_file_mappings 9
|
||||
global_models 11
|
||||
ldap_configs 15
|
||||
management_tokens 14
|
||||
models 17
|
||||
oauth_providers 15
|
||||
payment_callbacks 13
|
||||
payment_orders 18
|
||||
provider_api_keys 52
|
||||
provider_endpoints 17
|
||||
provider_usage_tracking 12
|
||||
providers 23
|
||||
proxy_node_events 5
|
||||
proxy_nodes 29
|
||||
refund_requests 24
|
||||
request_candidates 24
|
||||
stats_daily 29
|
||||
stats_daily_api_key 14
|
||||
stats_daily_error 8
|
||||
stats_daily_model 12
|
||||
stats_daily_provider 11
|
||||
stats_hourly 16
|
||||
stats_hourly_model 10
|
||||
stats_hourly_provider 9
|
||||
stats_hourly_user 11
|
||||
stats_summary 17
|
||||
stats_user_daily 14
|
||||
system_configs 6
|
||||
usage 85
|
||||
user_model_usage_counts 6
|
||||
user_oauth_links 9
|
||||
user_preferences 13
|
||||
user_sessions 22
|
||||
users 19
|
||||
video_tasks 50
|
||||
wallet_daily_usage_ledgers 15
|
||||
wallet_transactions 16
|
||||
wallets 14
|
||||
|
@@ -1,6 +1,160 @@
|
||||
use sqlx::PgPool;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use sqlx::{
|
||||
migrate::{Migrate, MigrateError, Migrator},
|
||||
PgPool,
|
||||
};
|
||||
use tracing::{error, info, warn};
|
||||
|
||||
static MIGRATOR: Migrator = sqlx::migrate!("./migrations");
|
||||
|
||||
/// Run all pending migrations embedded at compile time from `migrations/`.
|
||||
pub async fn run_migrations(pool: &PgPool) -> Result<(), sqlx::migrate::MigrateError> {
|
||||
sqlx::migrate!("./migrations").run(pool).await
|
||||
pub async fn run_migrations(pool: &PgPool) -> Result<(), MigrateError> {
|
||||
let mut conn = pool.acquire().await?;
|
||||
|
||||
if MIGRATOR.locking {
|
||||
conn.lock().await?;
|
||||
}
|
||||
|
||||
let result = run_migrations_locked(&mut *conn).await;
|
||||
|
||||
if MIGRATOR.locking {
|
||||
match conn.unlock().await {
|
||||
Ok(()) => {}
|
||||
Err(unlock_error) if result.is_ok() => return Err(unlock_error),
|
||||
Err(unlock_error) => {
|
||||
warn!(
|
||||
error = %unlock_error,
|
||||
"database migration lock release failed after migration error"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
async fn run_migrations_locked<C>(conn: &mut C) -> Result<(), MigrateError>
|
||||
where
|
||||
C: Migrate,
|
||||
{
|
||||
conn.ensure_migrations_table().await?;
|
||||
|
||||
if let Some(version) = conn.dirty_version().await? {
|
||||
error!(version, "database migration state is dirty");
|
||||
return Err(MigrateError::Dirty(version));
|
||||
}
|
||||
|
||||
let applied_migrations = conn.list_applied_migrations().await?;
|
||||
validate_applied_migrations(&applied_migrations)?;
|
||||
|
||||
let known_versions: HashSet<_> = MIGRATOR
|
||||
.iter()
|
||||
.filter(|migration| migration.migration_type.is_up_migration())
|
||||
.map(|migration| migration.version)
|
||||
.collect();
|
||||
let applied_migrations_by_version: HashMap<_, _> = applied_migrations
|
||||
.into_iter()
|
||||
.map(|migration| (migration.version, migration))
|
||||
.collect();
|
||||
|
||||
let pending_migrations: Vec<_> = MIGRATOR
|
||||
.iter()
|
||||
.filter(|migration| migration.migration_type.is_up_migration())
|
||||
.filter(|migration| !applied_migrations_by_version.contains_key(&migration.version))
|
||||
.collect();
|
||||
|
||||
let total_migrations = known_versions.len();
|
||||
let applied_count = total_migrations.saturating_sub(pending_migrations.len());
|
||||
|
||||
if pending_migrations.is_empty() {
|
||||
info!(
|
||||
total_migrations,
|
||||
applied_migrations = applied_count,
|
||||
pending_migrations = 0,
|
||||
"database migrations already up to date"
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
info!(
|
||||
total_migrations,
|
||||
applied_migrations = applied_count,
|
||||
pending_migrations = pending_migrations.len(),
|
||||
"database migrations pending"
|
||||
);
|
||||
|
||||
for (index, migration) in pending_migrations.iter().enumerate() {
|
||||
let current = index + 1;
|
||||
let total = pending_migrations.len();
|
||||
|
||||
info!(
|
||||
current,
|
||||
total,
|
||||
version = migration.version,
|
||||
description = %migration.description,
|
||||
"applying database migration"
|
||||
);
|
||||
|
||||
let elapsed = conn.apply(migration).await?;
|
||||
|
||||
info!(
|
||||
current,
|
||||
total,
|
||||
version = migration.version,
|
||||
description = %migration.description,
|
||||
elapsed_ms = elapsed.as_millis() as u64,
|
||||
"applied database migration"
|
||||
);
|
||||
}
|
||||
|
||||
info!(
|
||||
total_migrations,
|
||||
applied_migrations = total_migrations,
|
||||
pending_migrations = 0,
|
||||
"database migrations complete"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_applied_migrations(
|
||||
applied_migrations: &[sqlx::migrate::AppliedMigration],
|
||||
) -> Result<(), MigrateError> {
|
||||
if MIGRATOR.ignore_missing {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let known_versions: HashSet<_> = MIGRATOR.iter().map(|migration| migration.version).collect();
|
||||
|
||||
for applied_migration in applied_migrations {
|
||||
if !known_versions.contains(&applied_migration.version) {
|
||||
error!(
|
||||
version = applied_migration.version,
|
||||
"applied database migration is missing from embedded migrations"
|
||||
);
|
||||
return Err(MigrateError::VersionMissing(applied_migration.version));
|
||||
}
|
||||
}
|
||||
|
||||
for migration in MIGRATOR
|
||||
.iter()
|
||||
.filter(|migration| migration.migration_type.is_up_migration())
|
||||
{
|
||||
if let Some(applied_migration) = applied_migrations
|
||||
.iter()
|
||||
.find(|applied_migration| applied_migration.version == migration.version)
|
||||
{
|
||||
if migration.checksum != applied_migration.checksum {
|
||||
error!(
|
||||
version = migration.version,
|
||||
description = %migration.description,
|
||||
"database migration checksum mismatch detected"
|
||||
);
|
||||
return Err(MigrateError::VersionMismatch(migration.version));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -258,6 +258,20 @@ impl UsageWriteRepository for InMemoryUsageReadRepository {
|
||||
.map(|existing| existing.cache_creation_input_tokens)
|
||||
.unwrap_or_default()
|
||||
}),
|
||||
cache_creation_ephemeral_5m_input_tokens: usage
|
||||
.cache_creation_ephemeral_5m_input_tokens
|
||||
.unwrap_or_else(|| {
|
||||
existing
|
||||
.map(|existing| existing.cache_creation_ephemeral_5m_input_tokens)
|
||||
.unwrap_or_default()
|
||||
}),
|
||||
cache_creation_ephemeral_1h_input_tokens: usage
|
||||
.cache_creation_ephemeral_1h_input_tokens
|
||||
.unwrap_or_else(|| {
|
||||
existing
|
||||
.map(|existing| existing.cache_creation_ephemeral_1h_input_tokens)
|
||||
.unwrap_or_default()
|
||||
}),
|
||||
cache_read_input_tokens: usage.cache_read_input_tokens.unwrap_or_else(|| {
|
||||
existing
|
||||
.map(|existing| existing.cache_read_input_tokens)
|
||||
@@ -427,6 +441,8 @@ mod tests {
|
||||
output_tokens: Some(20),
|
||||
total_tokens: None,
|
||||
cache_creation_input_tokens: None,
|
||||
cache_creation_ephemeral_5m_input_tokens: None,
|
||||
cache_creation_ephemeral_1h_input_tokens: None,
|
||||
cache_read_input_tokens: None,
|
||||
cache_creation_cost_usd: None,
|
||||
cache_read_cost_usd: None,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use async_trait::async_trait;
|
||||
use futures_util::future::BoxFuture;
|
||||
use serde_json::Value;
|
||||
use sqlx::{PgPool, Postgres, QueryBuilder, Row};
|
||||
use uuid::Uuid;
|
||||
|
||||
@@ -37,6 +38,8 @@ SELECT
|
||||
output_tokens,
|
||||
total_tokens,
|
||||
COALESCE(cache_creation_input_tokens, 0) AS cache_creation_input_tokens,
|
||||
COALESCE(cache_creation_input_tokens_5m, 0) AS cache_creation_ephemeral_5m_input_tokens,
|
||||
COALESCE(cache_creation_input_tokens_1h, 0) AS cache_creation_ephemeral_1h_input_tokens,
|
||||
COALESCE(cache_read_input_tokens, 0) AS cache_read_input_tokens,
|
||||
COALESCE(CAST(cache_creation_cost_usd AS DOUBLE PRECISION), 0) AS cache_creation_cost_usd,
|
||||
COALESCE(CAST(cache_read_cost_usd AS DOUBLE PRECISION), 0) AS cache_read_cost_usd,
|
||||
@@ -94,6 +97,8 @@ SELECT
|
||||
output_tokens,
|
||||
total_tokens,
|
||||
COALESCE(cache_creation_input_tokens, 0) AS cache_creation_input_tokens,
|
||||
COALESCE(cache_creation_input_tokens_5m, 0) AS cache_creation_ephemeral_5m_input_tokens,
|
||||
COALESCE(cache_creation_input_tokens_1h, 0) AS cache_creation_ephemeral_1h_input_tokens,
|
||||
COALESCE(cache_read_input_tokens, 0) AS cache_read_input_tokens,
|
||||
COALESCE(CAST(cache_creation_cost_usd AS DOUBLE PRECISION), 0) AS cache_creation_cost_usd,
|
||||
COALESCE(CAST(cache_read_cost_usd AS DOUBLE PRECISION), 0) AS cache_read_cost_usd,
|
||||
@@ -181,6 +186,8 @@ SELECT
|
||||
output_tokens,
|
||||
total_tokens,
|
||||
COALESCE(cache_creation_input_tokens, 0) AS cache_creation_input_tokens,
|
||||
COALESCE(cache_creation_input_tokens_5m, 0) AS cache_creation_ephemeral_5m_input_tokens,
|
||||
COALESCE(cache_creation_input_tokens_1h, 0) AS cache_creation_ephemeral_1h_input_tokens,
|
||||
COALESCE(cache_read_input_tokens, 0) AS cache_read_input_tokens,
|
||||
COALESCE(CAST(cache_creation_cost_usd AS DOUBLE PRECISION), 0) AS cache_creation_cost_usd,
|
||||
COALESCE(CAST(cache_read_cost_usd AS DOUBLE PRECISION), 0) AS cache_read_cost_usd,
|
||||
@@ -194,15 +201,15 @@ SELECT
|
||||
first_byte_time_ms,
|
||||
status,
|
||||
billing_status,
|
||||
NULL::jsonb AS request_headers,
|
||||
NULL::jsonb AS request_body,
|
||||
NULL::jsonb AS provider_request_headers,
|
||||
NULL::jsonb AS provider_request_body,
|
||||
NULL::jsonb AS response_headers,
|
||||
NULL::jsonb AS response_body,
|
||||
NULL::jsonb AS client_response_headers,
|
||||
NULL::jsonb AS client_response_body,
|
||||
NULL::jsonb AS request_metadata,
|
||||
NULL::json AS request_headers,
|
||||
NULL::json AS request_body,
|
||||
NULL::json AS provider_request_headers,
|
||||
NULL::json AS provider_request_body,
|
||||
NULL::json AS response_headers,
|
||||
NULL::json AS response_body,
|
||||
NULL::json AS client_response_headers,
|
||||
NULL::json AS client_response_body,
|
||||
NULL::json AS request_metadata,
|
||||
CAST(EXTRACT(EPOCH FROM created_at) AS BIGINT) AS created_at_unix_ms,
|
||||
CAST(EXTRACT(EPOCH FROM COALESCE(finalized_at, created_at)) AS BIGINT) AS updated_at_unix_secs,
|
||||
CAST(EXTRACT(EPOCH FROM finalized_at) AS BIGINT) AS finalized_at_unix_secs
|
||||
@@ -236,6 +243,8 @@ SELECT
|
||||
output_tokens,
|
||||
total_tokens,
|
||||
COALESCE(cache_creation_input_tokens, 0) AS cache_creation_input_tokens,
|
||||
COALESCE(cache_creation_input_tokens_5m, 0) AS cache_creation_ephemeral_5m_input_tokens,
|
||||
COALESCE(cache_creation_input_tokens_1h, 0) AS cache_creation_ephemeral_1h_input_tokens,
|
||||
COALESCE(cache_read_input_tokens, 0) AS cache_read_input_tokens,
|
||||
COALESCE(CAST(cache_creation_cost_usd AS DOUBLE PRECISION), 0) AS cache_creation_cost_usd,
|
||||
COALESCE(CAST(cache_read_cost_usd AS DOUBLE PRECISION), 0) AS cache_read_cost_usd,
|
||||
@@ -249,15 +258,15 @@ SELECT
|
||||
first_byte_time_ms,
|
||||
status,
|
||||
billing_status,
|
||||
NULL::jsonb AS request_headers,
|
||||
NULL::jsonb AS request_body,
|
||||
NULL::jsonb AS provider_request_headers,
|
||||
NULL::jsonb AS provider_request_body,
|
||||
NULL::jsonb AS response_headers,
|
||||
NULL::jsonb AS response_body,
|
||||
NULL::jsonb AS client_response_headers,
|
||||
NULL::jsonb AS client_response_body,
|
||||
NULL::jsonb AS request_metadata,
|
||||
NULL::json AS request_headers,
|
||||
NULL::json AS request_body,
|
||||
NULL::json AS provider_request_headers,
|
||||
NULL::json AS provider_request_body,
|
||||
NULL::json AS response_headers,
|
||||
NULL::json AS response_body,
|
||||
NULL::json AS client_response_headers,
|
||||
NULL::json AS client_response_body,
|
||||
NULL::json AS request_metadata,
|
||||
CAST(EXTRACT(EPOCH FROM created_at) AS BIGINT) AS created_at_unix_ms,
|
||||
CAST(EXTRACT(EPOCH FROM COALESCE(finalized_at, created_at)) AS BIGINT) AS updated_at_unix_secs,
|
||||
CAST(EXTRACT(EPOCH FROM finalized_at) AS BIGINT) AS finalized_at_unix_secs
|
||||
@@ -291,6 +300,8 @@ INSERT INTO "usage" (
|
||||
output_tokens,
|
||||
total_tokens,
|
||||
cache_creation_input_tokens,
|
||||
cache_creation_input_tokens_5m,
|
||||
cache_creation_input_tokens_1h,
|
||||
cache_read_input_tokens,
|
||||
cache_creation_cost_usd,
|
||||
cache_read_cost_usd,
|
||||
@@ -344,10 +355,10 @@ INSERT INTO "usage" (
|
||||
COALESCE($26, 0),
|
||||
COALESCE($27, 0),
|
||||
COALESCE($28, 0),
|
||||
$29,
|
||||
COALESCE($30, 0),
|
||||
COALESCE($29, 0),
|
||||
$30,
|
||||
COALESCE($31, 0),
|
||||
$32,
|
||||
COALESCE($32, 0),
|
||||
$33,
|
||||
$34,
|
||||
$35,
|
||||
@@ -356,18 +367,20 @@ INSERT INTO "usage" (
|
||||
$38,
|
||||
$39,
|
||||
$40,
|
||||
$41,
|
||||
$42,
|
||||
$43,
|
||||
$44,
|
||||
$45,
|
||||
$46,
|
||||
$47,
|
||||
$41::json,
|
||||
$42::json,
|
||||
$43::json,
|
||||
$44::json,
|
||||
$45::json,
|
||||
$46::json,
|
||||
$47::json,
|
||||
$48::json,
|
||||
$49::json,
|
||||
CASE
|
||||
WHEN $48 IS NULL THEN NULL
|
||||
ELSE TO_TIMESTAMP($48::double precision)
|
||||
WHEN $50 IS NULL THEN NULL
|
||||
ELSE TO_TIMESTAMP($50::double precision)
|
||||
END,
|
||||
COALESCE(TO_TIMESTAMP($49::double precision), NOW())
|
||||
COALESCE(TO_TIMESTAMP($51::double precision), NOW())
|
||||
)
|
||||
ON CONFLICT (request_id)
|
||||
DO UPDATE SET
|
||||
@@ -394,6 +407,8 @@ DO UPDATE SET
|
||||
output_tokens = CASE WHEN "usage".billing_status = 'pending' THEN COALESCE(EXCLUDED.output_tokens, "usage".output_tokens) ELSE "usage".output_tokens END,
|
||||
total_tokens = CASE WHEN "usage".billing_status = 'pending' THEN COALESCE(EXCLUDED.total_tokens, "usage".total_tokens) ELSE "usage".total_tokens END,
|
||||
cache_creation_input_tokens = CASE WHEN "usage".billing_status = 'pending' THEN COALESCE(EXCLUDED.cache_creation_input_tokens, "usage".cache_creation_input_tokens) ELSE "usage".cache_creation_input_tokens END,
|
||||
cache_creation_input_tokens_5m = CASE WHEN "usage".billing_status = 'pending' THEN COALESCE(EXCLUDED.cache_creation_input_tokens_5m, "usage".cache_creation_input_tokens_5m) ELSE "usage".cache_creation_input_tokens_5m END,
|
||||
cache_creation_input_tokens_1h = CASE WHEN "usage".billing_status = 'pending' THEN COALESCE(EXCLUDED.cache_creation_input_tokens_1h, "usage".cache_creation_input_tokens_1h) ELSE "usage".cache_creation_input_tokens_1h END,
|
||||
cache_read_input_tokens = CASE WHEN "usage".billing_status = 'pending' THEN COALESCE(EXCLUDED.cache_read_input_tokens, "usage".cache_read_input_tokens) ELSE "usage".cache_read_input_tokens END,
|
||||
cache_creation_cost_usd = CASE WHEN "usage".billing_status = 'pending' THEN COALESCE(EXCLUDED.cache_creation_cost_usd, "usage".cache_creation_cost_usd) ELSE "usage".cache_creation_cost_usd END,
|
||||
cache_read_cost_usd = CASE WHEN "usage".billing_status = 'pending' THEN COALESCE(EXCLUDED.cache_read_cost_usd, "usage".cache_read_cost_usd) ELSE "usage".cache_read_cost_usd END,
|
||||
@@ -443,6 +458,8 @@ RETURNING
|
||||
output_tokens,
|
||||
total_tokens,
|
||||
COALESCE(cache_creation_input_tokens, 0) AS cache_creation_input_tokens,
|
||||
COALESCE(cache_creation_input_tokens_5m, 0) AS cache_creation_ephemeral_5m_input_tokens,
|
||||
COALESCE(cache_creation_input_tokens_1h, 0) AS cache_creation_ephemeral_1h_input_tokens,
|
||||
COALESCE(cache_read_input_tokens, 0) AS cache_read_input_tokens,
|
||||
COALESCE(CAST(cache_creation_cost_usd AS DOUBLE PRECISION), 0) AS cache_creation_cost_usd,
|
||||
COALESCE(CAST(cache_read_cost_usd AS DOUBLE PRECISION), 0) AS cache_read_cost_usd,
|
||||
@@ -653,6 +670,19 @@ impl SqlxUsageReadRepository {
|
||||
self.tx_runner
|
||||
.run_read_write(|tx| {
|
||||
Box::pin(async move {
|
||||
let request_headers_json = json_bind_text(usage.request_headers.as_ref())?;
|
||||
let request_body_json = json_bind_text(usage.request_body.as_ref())?;
|
||||
let provider_request_headers_json =
|
||||
json_bind_text(usage.provider_request_headers.as_ref())?;
|
||||
let provider_request_body_json =
|
||||
json_bind_text(usage.provider_request_body.as_ref())?;
|
||||
let response_headers_json = json_bind_text(usage.response_headers.as_ref())?;
|
||||
let response_body_json = json_bind_text(usage.response_body.as_ref())?;
|
||||
let client_response_headers_json =
|
||||
json_bind_text(usage.client_response_headers.as_ref())?;
|
||||
let client_response_body_json =
|
||||
json_bind_text(usage.client_response_body.as_ref())?;
|
||||
let request_metadata_json = json_bind_text(usage.request_metadata.as_ref())?;
|
||||
let row = sqlx::query(UPSERT_SQL)
|
||||
.bind(Uuid::new_v4().to_string())
|
||||
.bind(&usage.request_id)
|
||||
@@ -690,6 +720,18 @@ impl SqlxUsageReadRepository {
|
||||
.transpose()?,
|
||||
)
|
||||
.bind(usage.cache_creation_input_tokens.map(to_i32).transpose()?)
|
||||
.bind(
|
||||
usage
|
||||
.cache_creation_ephemeral_5m_input_tokens
|
||||
.map(to_i32)
|
||||
.transpose()?,
|
||||
)
|
||||
.bind(
|
||||
usage
|
||||
.cache_creation_ephemeral_1h_input_tokens
|
||||
.map(to_i32)
|
||||
.transpose()?,
|
||||
)
|
||||
.bind(usage.cache_read_input_tokens.map(to_i32).transpose()?)
|
||||
.bind(usage.cache_creation_cost_usd)
|
||||
.bind(usage.cache_read_cost_usd)
|
||||
@@ -703,15 +745,15 @@ impl SqlxUsageReadRepository {
|
||||
.bind(usage.first_byte_time_ms.map(to_i32).transpose()?)
|
||||
.bind(&usage.status)
|
||||
.bind(&usage.billing_status)
|
||||
.bind(&usage.request_headers)
|
||||
.bind(&usage.request_body)
|
||||
.bind(&usage.provider_request_headers)
|
||||
.bind(&usage.provider_request_body)
|
||||
.bind(&usage.response_headers)
|
||||
.bind(&usage.response_body)
|
||||
.bind(&usage.client_response_headers)
|
||||
.bind(&usage.client_response_body)
|
||||
.bind(&usage.request_metadata)
|
||||
.bind(&request_headers_json)
|
||||
.bind(&request_body_json)
|
||||
.bind(&provider_request_headers_json)
|
||||
.bind(&provider_request_body_json)
|
||||
.bind(&response_headers_json)
|
||||
.bind(&response_body_json)
|
||||
.bind(&client_response_headers_json)
|
||||
.bind(&client_response_body_json)
|
||||
.bind(&request_metadata_json)
|
||||
.bind(usage.finalized_at_unix_secs.map(|value| value as f64))
|
||||
.bind(usage.created_at_unix_ms.map(|value| value as f64))
|
||||
.fetch_one(&mut **tx)
|
||||
@@ -826,6 +868,18 @@ fn map_usage_row(row: &sqlx::postgres::PgRow) -> Result<StoredRequestUsageAudit,
|
||||
.map(|value| to_u64(value, "usage.cache_creation_input_tokens"))
|
||||
.transpose()?
|
||||
.unwrap_or_default();
|
||||
usage.cache_creation_ephemeral_5m_input_tokens = row
|
||||
.try_get::<Option<i32>, _>("cache_creation_ephemeral_5m_input_tokens")
|
||||
.map_postgres_err()?
|
||||
.map(|value| to_u64(value, "usage.cache_creation_ephemeral_5m_input_tokens"))
|
||||
.transpose()?
|
||||
.unwrap_or_default();
|
||||
usage.cache_creation_ephemeral_1h_input_tokens = row
|
||||
.try_get::<Option<i32>, _>("cache_creation_ephemeral_1h_input_tokens")
|
||||
.map_postgres_err()?
|
||||
.map(|value| to_u64(value, "usage.cache_creation_ephemeral_1h_input_tokens"))
|
||||
.transpose()?
|
||||
.unwrap_or_default();
|
||||
usage.cache_read_input_tokens = row
|
||||
.try_get::<Option<i32>, _>("cache_read_input_tokens")
|
||||
.map_postgres_err()?
|
||||
@@ -862,6 +916,16 @@ fn to_u64(value: i32, field_name: &str) -> Result<u64, DataLayerError> {
|
||||
.map_err(|_| DataLayerError::UnexpectedValue(format!("invalid {field_name}: {value}")))
|
||||
}
|
||||
|
||||
fn json_bind_text(value: Option<&Value>) -> Result<Option<String>, DataLayerError> {
|
||||
value
|
||||
.map(|value| {
|
||||
serde_json::to_string(value).map_err(|err| {
|
||||
DataLayerError::UnexpectedValue(format!("failed to serialize usage json: {err}"))
|
||||
})
|
||||
})
|
||||
.transpose()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::SqlxUsageReadRepository;
|
||||
@@ -930,6 +994,8 @@ mod tests {
|
||||
output_tokens: Some(20),
|
||||
total_tokens: Some(30),
|
||||
cache_creation_input_tokens: None,
|
||||
cache_creation_ephemeral_5m_input_tokens: None,
|
||||
cache_creation_ephemeral_1h_input_tokens: None,
|
||||
cache_read_input_tokens: None,
|
||||
cache_creation_cost_usd: None,
|
||||
cache_read_cost_usd: None,
|
||||
@@ -980,10 +1046,32 @@ mod tests {
|
||||
assert!(super::LIST_RECENT_USAGE_AUDITS_PREFIX.contains("FROM \"usage\""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn usage_sql_uses_json_null_placeholders_for_usage_payload_columns() {
|
||||
assert!(super::LIST_USAGE_AUDITS_PREFIX.contains("NULL::json AS request_headers"));
|
||||
assert!(super::LIST_USAGE_AUDITS_PREFIX.contains("NULL::json AS provider_request_body"));
|
||||
assert!(super::LIST_RECENT_USAGE_AUDITS_PREFIX.contains("NULL::json AS request_headers"));
|
||||
assert!(
|
||||
super::LIST_RECENT_USAGE_AUDITS_PREFIX.contains("NULL::json AS provider_request_body")
|
||||
);
|
||||
assert!(!super::LIST_USAGE_AUDITS_PREFIX.contains("NULL::jsonb"));
|
||||
assert!(!super::LIST_RECENT_USAGE_AUDITS_PREFIX.contains("NULL::jsonb"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn usage_sql_casts_json_payload_bind_parameters_explicitly() {
|
||||
for placeholder in 41..=49 {
|
||||
assert!(
|
||||
super::UPSERT_SQL.contains(format!("${placeholder}::json").as_str()),
|
||||
"missing ::json cast for placeholder ${placeholder}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn usage_sql_insert_values_aligns_request_metadata_and_timestamps() {
|
||||
assert!(super::UPSERT_SQL.contains("\n $46,\n $47,\n CASE"));
|
||||
assert!(super::UPSERT_SQL.contains("WHEN $48 IS NULL THEN NULL"));
|
||||
assert!(super::UPSERT_SQL.contains("TO_TIMESTAMP($49::double precision)"));
|
||||
assert!(super::UPSERT_SQL.contains("\n $48::json,\n $49::json,\n CASE"));
|
||||
assert!(super::UPSERT_SQL.contains("WHEN $50 IS NULL THEN NULL"));
|
||||
assert!(super::UPSERT_SQL.contains("TO_TIMESTAMP($51::double precision)"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user