diff --git a/drizzle/0105_chief_rocket_racer.sql b/drizzle/0105_chief_rocket_racer.sql new file mode 100644 index 000000000..14aa3e620 --- /dev/null +++ b/drizzle/0105_chief_rocket_racer.sql @@ -0,0 +1 @@ +ALTER TABLE "system_settings" ADD COLUMN "enable_thinking_effort_conflict_rectifier" boolean DEFAULT true NOT NULL; \ No newline at end of file diff --git a/drizzle/meta/0105_snapshot.json b/drizzle/meta/0105_snapshot.json new file mode 100644 index 000000000..41d3c35e7 --- /dev/null +++ b/drizzle/meta/0105_snapshot.json @@ -0,0 +1,4535 @@ +{ + "id": "e61c4e85-4edf-4d73-b2e3-5326af94eba0", + "prevId": "e287317d-0fc7-4491-960d-b22636dc9471", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.audit_log": { + "name": "audit_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "action_category": { + "name": "action_category", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "action_type": { + "name": "action_type", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "target_type": { + "name": "target_type", + "type": "varchar(32)", + "primaryKey": false, + "notNull": false + }, + "target_id": { + "name": "target_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "target_name": { + "name": "target_name", + "type": "varchar(256)", + "primaryKey": false, + "notNull": false + }, + "before_value": { + "name": "before_value", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "after_value": { + "name": "after_value", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "operator_user_id": { + "name": "operator_user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "operator_user_name": { + "name": "operator_user_name", + "type": "varchar(128)", + "primaryKey": false, + "notNull": false + }, + "operator_key_id": { + "name": "operator_key_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "operator_key_name": { + "name": "operator_key_name", + "type": "varchar(128)", + "primaryKey": false, + "notNull": false + }, + "operator_ip": { + "name": "operator_ip", + "type": "varchar(45)", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "varchar(512)", + "primaryKey": false, + "notNull": false + }, + "success": { + "name": "success", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_audit_log_category_created_at": { + "name": "idx_audit_log_category_created_at", + "columns": [ + { + "expression": "action_category", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_audit_log_operator_user_created_at": { + "name": "idx_audit_log_operator_user_created_at", + "columns": [ + { + "expression": "operator_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"audit_log\".\"operator_user_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_audit_log_operator_ip_created_at": { + "name": "idx_audit_log_operator_ip_created_at", + "columns": [ + { + "expression": "operator_ip", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"audit_log\".\"operator_ip\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_audit_log_target": { + "name": "idx_audit_log_target", + "columns": [ + { + "expression": "target_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"audit_log\".\"target_type\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_audit_log_created_at_id": { + "name": "idx_audit_log_created_at_id", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.error_rules": { + "name": "error_rules", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "pattern": { + "name": "pattern", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "match_type": { + "name": "match_type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'regex'" + }, + "category": { + "name": "category", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "override_response": { + "name": "override_response", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "override_status_code": { + "name": "override_status_code", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "idx_error_rules_enabled": { + "name": "idx_error_rules_enabled", + "columns": [ + { + "expression": "is_enabled", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "priority", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "unique_pattern": { + "name": "unique_pattern", + "columns": [ + { + "expression": "pattern", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_category": { + "name": "idx_category", + "columns": [ + { + "expression": "category", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_match_type": { + "name": "idx_match_type", + "columns": [ + { + "expression": "match_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.keys": { + "name": "keys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "can_login_web_ui": { + "name": "can_login_web_ui", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "limit_5h_usd": { + "name": "limit_5h_usd", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "limit_5h_reset_mode": { + "name": "limit_5h_reset_mode", + "type": "daily_reset_mode", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'rolling'" + }, + "limit_daily_usd": { + "name": "limit_daily_usd", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "daily_reset_mode": { + "name": "daily_reset_mode", + "type": "daily_reset_mode", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'fixed'" + }, + "daily_reset_time": { + "name": "daily_reset_time", + "type": "varchar(5)", + "primaryKey": false, + "notNull": true, + "default": "'00:00'" + }, + "limit_weekly_usd": { + "name": "limit_weekly_usd", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "limit_monthly_usd": { + "name": "limit_monthly_usd", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "limit_total_usd": { + "name": "limit_total_usd", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "cost_reset_at": { + "name": "cost_reset_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "limit_concurrent_sessions": { + "name": "limit_concurrent_sessions", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "provider_group": { + "name": "provider_group", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false, + "default": "'default'" + }, + "cache_ttl_preference": { + "name": "cache_ttl_preference", + "type": "varchar(10)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_keys_user_id": { + "name": "idx_keys_user_id", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_keys_key": { + "name": "idx_keys_key", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_keys_created_at": { + "name": "idx_keys_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_keys_deleted_at": { + "name": "idx_keys_deleted_at", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.message_request": { + "name": "message_request", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "varchar(128)", + "primaryKey": false, + "notNull": false + }, + "duration_ms": { + "name": "duration_ms", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cost_usd": { + "name": "cost_usd", + "type": "numeric(21, 15)", + "primaryKey": false, + "notNull": false, + "default": "'0'" + }, + "cost_multiplier": { + "name": "cost_multiplier", + "type": "numeric(10, 4)", + "primaryKey": false, + "notNull": false + }, + "group_cost_multiplier": { + "name": "group_cost_multiplier", + "type": "numeric(10, 4)", + "primaryKey": false, + "notNull": false + }, + "cost_breakdown": { + "name": "cost_breakdown", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "session_id": { + "name": "session_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "request_sequence": { + "name": "request_sequence", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 1 + }, + "provider_chain": { + "name": "provider_chain", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "status_code": { + "name": "status_code", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "api_type": { + "name": "api_type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "endpoint": { + "name": "endpoint", + "type": "varchar(256)", + "primaryKey": false, + "notNull": false + }, + "original_model": { + "name": "original_model", + "type": "varchar(128)", + "primaryKey": false, + "notNull": false + }, + "actual_response_model": { + "name": "actual_response_model", + "type": "varchar(128)", + "primaryKey": false, + "notNull": false + }, + "input_tokens": { + "name": "input_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "output_tokens": { + "name": "output_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "ttfb_ms": { + "name": "ttfb_ms", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cache_creation_input_tokens": { + "name": "cache_creation_input_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "cache_read_input_tokens": { + "name": "cache_read_input_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "cache_creation_5m_input_tokens": { + "name": "cache_creation_5m_input_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "cache_creation_1h_input_tokens": { + "name": "cache_creation_1h_input_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "cache_ttl_applied": { + "name": "cache_ttl_applied", + "type": "varchar(10)", + "primaryKey": false, + "notNull": false + }, + "context_1m_applied": { + "name": "context_1m_applied", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "swap_cache_ttl_applied": { + "name": "swap_cache_ttl_applied", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "special_settings": { + "name": "special_settings", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "hedge_losers": { + "name": "hedge_losers", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error_stack": { + "name": "error_stack", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error_cause": { + "name": "error_cause", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "blocked_by": { + "name": "blocked_by", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "blocked_reason": { + "name": "blocked_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "varchar(512)", + "primaryKey": false, + "notNull": false + }, + "client_ip": { + "name": "client_ip", + "type": "varchar(45)", + "primaryKey": false, + "notNull": false + }, + "messages_count": { + "name": "messages_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_message_request_user_date_cost": { + "name": "idx_message_request_user_date_cost", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "cost_usd", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"message_request\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_message_request_user_created_at_cost_stats": { + "name": "idx_message_request_user_created_at_cost_stats", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "cost_usd", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"message_request\".\"deleted_at\" IS NULL AND (\"message_request\".\"blocked_by\" IS NULL OR \"message_request\".\"blocked_by\" <> 'warmup')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_message_request_user_query": { + "name": "idx_message_request_user_query", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"message_request\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_message_request_provider_created_at_active": { + "name": "idx_message_request_provider_created_at_active", + "columns": [ + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"message_request\".\"deleted_at\" IS NULL AND (\"message_request\".\"blocked_by\" IS NULL OR \"message_request\".\"blocked_by\" <> 'warmup')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_message_request_provider_created_at_finalized_active": { + "name": "idx_message_request_provider_created_at_finalized_active", + "columns": [ + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"message_request\".\"deleted_at\" IS NULL AND \"message_request\".\"status_code\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_message_request_session_id": { + "name": "idx_message_request_session_id", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"message_request\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_message_request_session_id_prefix": { + "name": "idx_message_request_session_id_prefix", + "columns": [ + { + "expression": "\"session_id\" varchar_pattern_ops", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"message_request\".\"deleted_at\" IS NULL AND (\"message_request\".\"blocked_by\" IS NULL OR \"message_request\".\"blocked_by\" <> 'warmup')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_message_request_session_seq": { + "name": "idx_message_request_session_seq", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "request_sequence", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"message_request\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_message_request_endpoint": { + "name": "idx_message_request_endpoint", + "columns": [ + { + "expression": "endpoint", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"message_request\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_message_request_blocked_by": { + "name": "idx_message_request_blocked_by", + "columns": [ + { + "expression": "blocked_by", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"message_request\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_message_request_provider_id": { + "name": "idx_message_request_provider_id", + "columns": [ + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_message_request_user_id": { + "name": "idx_message_request_user_id", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_message_request_key": { + "name": "idx_message_request_key", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_message_request_key_created_at_id": { + "name": "idx_message_request_key_created_at_id", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"message_request\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_message_request_key_model_active": { + "name": "idx_message_request_key_model_active", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "model", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"message_request\".\"deleted_at\" IS NULL AND \"message_request\".\"model\" IS NOT NULL AND (\"message_request\".\"blocked_by\" IS NULL OR \"message_request\".\"blocked_by\" <> 'warmup')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_message_request_key_endpoint_active": { + "name": "idx_message_request_key_endpoint_active", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "endpoint", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"message_request\".\"deleted_at\" IS NULL AND \"message_request\".\"endpoint\" IS NOT NULL AND (\"message_request\".\"blocked_by\" IS NULL OR \"message_request\".\"blocked_by\" <> 'warmup')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_message_request_created_at_id_active": { + "name": "idx_message_request_created_at_id_active", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"message_request\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_message_request_model_active": { + "name": "idx_message_request_model_active", + "columns": [ + { + "expression": "model", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"message_request\".\"deleted_at\" IS NULL AND \"message_request\".\"model\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_message_request_status_code_active": { + "name": "idx_message_request_status_code_active", + "columns": [ + { + "expression": "status_code", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"message_request\".\"deleted_at\" IS NULL AND \"message_request\".\"status_code\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_message_request_created_at": { + "name": "idx_message_request_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_message_request_deleted_at": { + "name": "idx_message_request_deleted_at", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_message_request_key_last_active": { + "name": "idx_message_request_key_last_active", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"message_request\".\"deleted_at\" IS NULL AND (\"message_request\".\"blocked_by\" IS NULL OR \"message_request\".\"blocked_by\" <> 'warmup')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_message_request_key_cost_active": { + "name": "idx_message_request_key_cost_active", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "cost_usd", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"message_request\".\"deleted_at\" IS NULL AND (\"message_request\".\"blocked_by\" IS NULL OR \"message_request\".\"blocked_by\" <> 'warmup')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_message_request_session_user_info": { + "name": "idx_message_request_session_user_info", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"message_request\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_message_request_client_ip_created_at": { + "name": "idx_message_request_client_ip_created_at", + "columns": [ + { + "expression": "client_ip", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"message_request\".\"deleted_at\" IS NULL AND \"message_request\".\"client_ip\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.model_prices": { + "name": "model_prices", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "model_name": { + "name": "model_name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "price_data": { + "name": "price_data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'litellm'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "idx_model_prices_latest": { + "name": "idx_model_prices_latest", + "columns": [ + { + "expression": "model_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_model_prices_model_name": { + "name": "idx_model_prices_model_name", + "columns": [ + { + "expression": "model_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_model_prices_created_at": { + "name": "idx_model_prices_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_model_prices_source": { + "name": "idx_model_prices_source", + "columns": [ + { + "expression": "source", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notification_settings": { + "name": "notification_settings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "use_legacy_mode": { + "name": "use_legacy_mode", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "circuit_breaker_enabled": { + "name": "circuit_breaker_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "circuit_breaker_webhook": { + "name": "circuit_breaker_webhook", + "type": "varchar(512)", + "primaryKey": false, + "notNull": false + }, + "daily_leaderboard_enabled": { + "name": "daily_leaderboard_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "daily_leaderboard_webhook": { + "name": "daily_leaderboard_webhook", + "type": "varchar(512)", + "primaryKey": false, + "notNull": false + }, + "daily_leaderboard_time": { + "name": "daily_leaderboard_time", + "type": "varchar(10)", + "primaryKey": false, + "notNull": false, + "default": "'09:00'" + }, + "daily_leaderboard_top_n": { + "name": "daily_leaderboard_top_n", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 5 + }, + "cost_alert_enabled": { + "name": "cost_alert_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cost_alert_webhook": { + "name": "cost_alert_webhook", + "type": "varchar(512)", + "primaryKey": false, + "notNull": false + }, + "cost_alert_threshold": { + "name": "cost_alert_threshold", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": false, + "default": "'0.80'" + }, + "cost_alert_check_interval": { + "name": "cost_alert_check_interval", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 60 + }, + "cache_hit_rate_alert_enabled": { + "name": "cache_hit_rate_alert_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cache_hit_rate_alert_webhook": { + "name": "cache_hit_rate_alert_webhook", + "type": "varchar(512)", + "primaryKey": false, + "notNull": false + }, + "cache_hit_rate_alert_window_mode": { + "name": "cache_hit_rate_alert_window_mode", + "type": "varchar(10)", + "primaryKey": false, + "notNull": false, + "default": "'auto'" + }, + "cache_hit_rate_alert_check_interval": { + "name": "cache_hit_rate_alert_check_interval", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 5 + }, + "cache_hit_rate_alert_historical_lookback_days": { + "name": "cache_hit_rate_alert_historical_lookback_days", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 7 + }, + "cache_hit_rate_alert_min_eligible_requests": { + "name": "cache_hit_rate_alert_min_eligible_requests", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 20 + }, + "cache_hit_rate_alert_min_eligible_tokens": { + "name": "cache_hit_rate_alert_min_eligible_tokens", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "cache_hit_rate_alert_abs_min": { + "name": "cache_hit_rate_alert_abs_min", + "type": "numeric(5, 4)", + "primaryKey": false, + "notNull": false, + "default": "'0.05'" + }, + "cache_hit_rate_alert_drop_rel": { + "name": "cache_hit_rate_alert_drop_rel", + "type": "numeric(5, 4)", + "primaryKey": false, + "notNull": false, + "default": "'0.3'" + }, + "cache_hit_rate_alert_drop_abs": { + "name": "cache_hit_rate_alert_drop_abs", + "type": "numeric(5, 4)", + "primaryKey": false, + "notNull": false, + "default": "'0.1'" + }, + "cache_hit_rate_alert_cooldown_minutes": { + "name": "cache_hit_rate_alert_cooldown_minutes", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 30 + }, + "cache_hit_rate_alert_top_n": { + "name": "cache_hit_rate_alert_top_n", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 10 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notification_target_bindings": { + "name": "notification_target_bindings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "notification_type": { + "name": "notification_type", + "type": "notification_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "target_id": { + "name": "target_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "schedule_cron": { + "name": "schedule_cron", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false + }, + "schedule_timezone": { + "name": "schedule_timezone", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "template_override": { + "name": "template_override", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "unique_notification_target_binding": { + "name": "unique_notification_target_binding", + "columns": [ + { + "expression": "notification_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_notification_bindings_type": { + "name": "idx_notification_bindings_type", + "columns": [ + { + "expression": "notification_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "is_enabled", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_notification_bindings_target": { + "name": "idx_notification_bindings_target", + "columns": [ + { + "expression": "target_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "is_enabled", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "notification_target_bindings_target_id_webhook_targets_id_fk": { + "name": "notification_target_bindings_target_id_webhook_targets_id_fk", + "tableFrom": "notification_target_bindings", + "tableTo": "webhook_targets", + "columnsFrom": [ + "target_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.provider_endpoint_probe_logs": { + "name": "provider_endpoint_probe_logs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "endpoint_id": { + "name": "endpoint_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'scheduled'" + }, + "ok": { + "name": "ok", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "status_code": { + "name": "status_code", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "latency_ms": { + "name": "latency_ms", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "error_type": { + "name": "error_type", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "idx_provider_endpoint_probe_logs_endpoint_created_at": { + "name": "idx_provider_endpoint_probe_logs_endpoint_created_at", + "columns": [ + { + "expression": "endpoint_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_provider_endpoint_probe_logs_created_at": { + "name": "idx_provider_endpoint_probe_logs_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "provider_endpoint_probe_logs_endpoint_id_provider_endpoints_id_fk": { + "name": "provider_endpoint_probe_logs_endpoint_id_provider_endpoints_id_fk", + "tableFrom": "provider_endpoint_probe_logs", + "tableTo": "provider_endpoints", + "columnsFrom": [ + "endpoint_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.provider_endpoints": { + "name": "provider_endpoints", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "vendor_id": { + "name": "vendor_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "provider_type": { + "name": "provider_type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'claude'" + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "last_probed_at": { + "name": "last_probed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_probe_ok": { + "name": "last_probe_ok", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "last_probe_status_code": { + "name": "last_probe_status_code", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "last_probe_latency_ms": { + "name": "last_probe_latency_ms", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "last_probe_error_type": { + "name": "last_probe_error_type", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "last_probe_error_message": { + "name": "last_probe_error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "uniq_provider_endpoints_vendor_type_url": { + "name": "uniq_provider_endpoints_vendor_type_url", + "columns": [ + { + "expression": "vendor_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "url", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"provider_endpoints\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_provider_endpoints_vendor_type": { + "name": "idx_provider_endpoints_vendor_type", + "columns": [ + { + "expression": "vendor_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"provider_endpoints\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_provider_endpoints_enabled": { + "name": "idx_provider_endpoints_enabled", + "columns": [ + { + "expression": "is_enabled", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "vendor_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"provider_endpoints\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_provider_endpoints_pick_enabled": { + "name": "idx_provider_endpoints_pick_enabled", + "columns": [ + { + "expression": "vendor_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "is_enabled", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sort_order", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"provider_endpoints\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_provider_endpoints_created_at": { + "name": "idx_provider_endpoints_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_provider_endpoints_deleted_at": { + "name": "idx_provider_endpoints_deleted_at", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "provider_endpoints_vendor_id_provider_vendors_id_fk": { + "name": "provider_endpoints_vendor_id_provider_vendors_id_fk", + "tableFrom": "provider_endpoints", + "tableTo": "provider_vendors", + "columnsFrom": [ + "vendor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.provider_groups": { + "name": "provider_groups", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(200)", + "primaryKey": false, + "notNull": true + }, + "cost_multiplier": { + "name": "cost_multiplier", + "type": "numeric(10, 4)", + "primaryKey": false, + "notNull": true, + "default": "'1.0'" + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "provider_groups_name_unique": { + "name": "provider_groups_name_unique", + "nullsNotDistinct": false, + "columns": [ + "name" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.provider_vendors": { + "name": "provider_vendors", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "website_domain": { + "name": "website_domain", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false + }, + "website_url": { + "name": "website_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "favicon_url": { + "name": "favicon_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "uniq_provider_vendors_website_domain": { + "name": "uniq_provider_vendors_website_domain", + "columns": [ + { + "expression": "website_domain", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_provider_vendors_created_at": { + "name": "idx_provider_vendors_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.providers": { + "name": "providers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "url": { + "name": "url", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "provider_vendor_id": { + "name": "provider_vendor_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "weight": { + "name": "weight", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "group_priorities": { + "name": "group_priorities", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'null'::jsonb" + }, + "cost_multiplier": { + "name": "cost_multiplier", + "type": "numeric(10, 4)", + "primaryKey": false, + "notNull": false, + "default": "'1.0'" + }, + "group_tag": { + "name": "group_tag", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "provider_type": { + "name": "provider_type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'claude'" + }, + "preserve_client_ip": { + "name": "preserve_client_ip", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "disable_session_reuse": { + "name": "disable_session_reuse", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "model_redirects": { + "name": "model_redirects", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "allowed_models": { + "name": "allowed_models", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'null'::jsonb" + }, + "allowed_clients": { + "name": "allowed_clients", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "blocked_clients": { + "name": "blocked_clients", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "active_time_start": { + "name": "active_time_start", + "type": "varchar(5)", + "primaryKey": false, + "notNull": false + }, + "active_time_end": { + "name": "active_time_end", + "type": "varchar(5)", + "primaryKey": false, + "notNull": false + }, + "codex_instructions_strategy": { + "name": "codex_instructions_strategy", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "default": "'auto'" + }, + "mcp_passthrough_type": { + "name": "mcp_passthrough_type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "mcp_passthrough_url": { + "name": "mcp_passthrough_url", + "type": "varchar(512)", + "primaryKey": false, + "notNull": false + }, + "limit_5h_usd": { + "name": "limit_5h_usd", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "limit_5h_reset_mode": { + "name": "limit_5h_reset_mode", + "type": "daily_reset_mode", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'rolling'" + }, + "limit_daily_usd": { + "name": "limit_daily_usd", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "daily_reset_mode": { + "name": "daily_reset_mode", + "type": "daily_reset_mode", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'fixed'" + }, + "daily_reset_time": { + "name": "daily_reset_time", + "type": "varchar(5)", + "primaryKey": false, + "notNull": true, + "default": "'00:00'" + }, + "limit_weekly_usd": { + "name": "limit_weekly_usd", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "limit_monthly_usd": { + "name": "limit_monthly_usd", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "limit_total_usd": { + "name": "limit_total_usd", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "total_cost_reset_at": { + "name": "total_cost_reset_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "limit_concurrent_sessions": { + "name": "limit_concurrent_sessions", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "max_retry_attempts": { + "name": "max_retry_attempts", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "circuit_breaker_failure_threshold": { + "name": "circuit_breaker_failure_threshold", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 5 + }, + "circuit_breaker_open_duration": { + "name": "circuit_breaker_open_duration", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 1800000 + }, + "circuit_breaker_half_open_success_threshold": { + "name": "circuit_breaker_half_open_success_threshold", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 2 + }, + "proxy_url": { + "name": "proxy_url", + "type": "varchar(512)", + "primaryKey": false, + "notNull": false + }, + "proxy_fallback_to_direct": { + "name": "proxy_fallback_to_direct", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "custom_headers": { + "name": "custom_headers", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "first_byte_timeout_streaming_ms": { + "name": "first_byte_timeout_streaming_ms", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "streaming_idle_timeout_ms": { + "name": "streaming_idle_timeout_ms", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "request_timeout_non_streaming_ms": { + "name": "request_timeout_non_streaming_ms", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "website_url": { + "name": "website_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "favicon_url": { + "name": "favicon_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cache_ttl_preference": { + "name": "cache_ttl_preference", + "type": "varchar(10)", + "primaryKey": false, + "notNull": false + }, + "swap_cache_ttl_billing": { + "name": "swap_cache_ttl_billing", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "context_1m_preference": { + "name": "context_1m_preference", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "codex_reasoning_effort_preference": { + "name": "codex_reasoning_effort_preference", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "codex_reasoning_summary_preference": { + "name": "codex_reasoning_summary_preference", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "codex_text_verbosity_preference": { + "name": "codex_text_verbosity_preference", + "type": "varchar(10)", + "primaryKey": false, + "notNull": false + }, + "codex_parallel_tool_calls_preference": { + "name": "codex_parallel_tool_calls_preference", + "type": "varchar(10)", + "primaryKey": false, + "notNull": false + }, + "codex_service_tier_preference": { + "name": "codex_service_tier_preference", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "anthropic_max_tokens_preference": { + "name": "anthropic_max_tokens_preference", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "anthropic_thinking_budget_preference": { + "name": "anthropic_thinking_budget_preference", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "anthropic_adaptive_thinking": { + "name": "anthropic_adaptive_thinking", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'null'::jsonb" + }, + "gemini_google_search_preference": { + "name": "gemini_google_search_preference", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "tpm": { + "name": "tpm", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "rpm": { + "name": "rpm", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "rpd": { + "name": "rpd", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "cc": { + "name": "cc", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_providers_enabled_priority": { + "name": "idx_providers_enabled_priority", + "columns": [ + { + "expression": "is_enabled", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "priority", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "weight", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"providers\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_providers_group": { + "name": "idx_providers_group", + "columns": [ + { + "expression": "group_tag", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"providers\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_providers_vendor_type_url_active": { + "name": "idx_providers_vendor_type_url_active", + "columns": [ + { + "expression": "provider_vendor_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "url", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"providers\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_providers_created_at": { + "name": "idx_providers_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_providers_deleted_at": { + "name": "idx_providers_deleted_at", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_providers_vendor_type": { + "name": "idx_providers_vendor_type", + "columns": [ + { + "expression": "provider_vendor_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"providers\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_providers_enabled_vendor_type": { + "name": "idx_providers_enabled_vendor_type", + "columns": [ + { + "expression": "provider_vendor_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"providers\".\"deleted_at\" IS NULL AND \"providers\".\"is_enabled\" = true AND \"providers\".\"provider_vendor_id\" IS NOT NULL AND \"providers\".\"provider_vendor_id\" > 0", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "providers_provider_vendor_id_provider_vendors_id_fk": { + "name": "providers_provider_vendor_id_provider_vendors_id_fk", + "tableFrom": "providers", + "tableTo": "provider_vendors", + "columnsFrom": [ + "provider_vendor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.request_filters": { + "name": "request_filters", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "action": { + "name": "action", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "match_type": { + "name": "match_type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "target": { + "name": "target", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "replacement": { + "name": "replacement", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "binding_type": { + "name": "binding_type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'global'" + }, + "provider_ids": { + "name": "provider_ids", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "group_tags": { + "name": "group_tags", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "rule_mode": { + "name": "rule_mode", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'simple'" + }, + "execution_phase": { + "name": "execution_phase", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'guard'" + }, + "operations": { + "name": "operations", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "idx_request_filters_enabled": { + "name": "idx_request_filters_enabled", + "columns": [ + { + "expression": "is_enabled", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "priority", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_request_filters_scope": { + "name": "idx_request_filters_scope", + "columns": [ + { + "expression": "scope", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_request_filters_action": { + "name": "idx_request_filters_action", + "columns": [ + { + "expression": "action", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_request_filters_binding": { + "name": "idx_request_filters_binding", + "columns": [ + { + "expression": "is_enabled", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "binding_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_request_filters_phase": { + "name": "idx_request_filters_phase", + "columns": [ + { + "expression": "is_enabled", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "execution_phase", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.sensitive_words": { + "name": "sensitive_words", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "word": { + "name": "word", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "match_type": { + "name": "match_type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'contains'" + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "idx_sensitive_words_enabled": { + "name": "idx_sensitive_words_enabled", + "columns": [ + { + "expression": "is_enabled", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "match_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_sensitive_words_created_at": { + "name": "idx_sensitive_words_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.system_settings": { + "name": "system_settings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "site_title": { + "name": "site_title", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true, + "default": "'Claude Code Hub'" + }, + "allow_global_usage_view": { + "name": "allow_global_usage_view", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "currency_display": { + "name": "currency_display", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true, + "default": "'USD'" + }, + "billing_model_source": { + "name": "billing_model_source", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'original'" + }, + "codex_priority_billing_source": { + "name": "codex_priority_billing_source", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'requested'" + }, + "bill_non_successful_requests": { + "name": "bill_non_successful_requests", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "bill_hedge_losers": { + "name": "bill_hedge_losers", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "timezone": { + "name": "timezone", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "enable_auto_cleanup": { + "name": "enable_auto_cleanup", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "cleanup_retention_days": { + "name": "cleanup_retention_days", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 30 + }, + "cleanup_schedule": { + "name": "cleanup_schedule", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "default": "'0 2 * * *'" + }, + "cleanup_batch_size": { + "name": "cleanup_batch_size", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 10000 + }, + "enable_client_version_check": { + "name": "enable_client_version_check", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "verbose_provider_error": { + "name": "verbose_provider_error", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "pass_through_upstream_error_message": { + "name": "pass_through_upstream_error_message", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "enable_http2": { + "name": "enable_http2", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enable_openai_responses_websocket": { + "name": "enable_openai_responses_websocket", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "enable_high_concurrency_mode": { + "name": "enable_high_concurrency_mode", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "intercept_anthropic_warmup_requests": { + "name": "intercept_anthropic_warmup_requests", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enable_thinking_signature_rectifier": { + "name": "enable_thinking_signature_rectifier", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "enable_thinking_budget_rectifier": { + "name": "enable_thinking_budget_rectifier", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "enable_thinking_effort_conflict_rectifier": { + "name": "enable_thinking_effort_conflict_rectifier", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "enable_billing_header_rectifier": { + "name": "enable_billing_header_rectifier", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "enable_response_input_rectifier": { + "name": "enable_response_input_rectifier", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "allow_non_conversation_endpoint_provider_fallback": { + "name": "allow_non_conversation_endpoint_provider_fallback", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "fake_streaming_whitelist": { + "name": "fake_streaming_whitelist", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "enable_codex_session_id_completion": { + "name": "enable_codex_session_id_completion", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "enable_claude_metadata_user_id_injection": { + "name": "enable_claude_metadata_user_id_injection", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "enable_response_fixer": { + "name": "enable_response_fixer", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "response_fixer_config": { + "name": "response_fixer_config", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{\"fixTruncatedJson\":true,\"fixSseFormat\":true,\"fixEncoding\":true,\"maxJsonDepth\":200,\"maxFixSize\":1048576}'::jsonb" + }, + "quota_db_refresh_interval_seconds": { + "name": "quota_db_refresh_interval_seconds", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 10 + }, + "quota_lease_percent_5h": { + "name": "quota_lease_percent_5h", + "type": "numeric(5, 4)", + "primaryKey": false, + "notNull": false, + "default": "'0.05'" + }, + "quota_lease_percent_daily": { + "name": "quota_lease_percent_daily", + "type": "numeric(5, 4)", + "primaryKey": false, + "notNull": false, + "default": "'0.05'" + }, + "quota_lease_percent_weekly": { + "name": "quota_lease_percent_weekly", + "type": "numeric(5, 4)", + "primaryKey": false, + "notNull": false, + "default": "'0.05'" + }, + "quota_lease_percent_monthly": { + "name": "quota_lease_percent_monthly", + "type": "numeric(5, 4)", + "primaryKey": false, + "notNull": false, + "default": "'0.05'" + }, + "quota_lease_cap_usd": { + "name": "quota_lease_cap_usd", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "ip_extraction_config": { + "name": "ip_extraction_config", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "ip_geo_lookup_enabled": { + "name": "ip_geo_lookup_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "public_status_window_hours": { + "name": "public_status_window_hours", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 24 + }, + "public_status_aggregation_interval_minutes": { + "name": "public_status_aggregation_interval_minutes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 5 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.usage_ledger": { + "name": "usage_ledger", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "request_id": { + "name": "request_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "final_provider_id": { + "name": "final_provider_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "varchar(128)", + "primaryKey": false, + "notNull": false + }, + "original_model": { + "name": "original_model", + "type": "varchar(128)", + "primaryKey": false, + "notNull": false + }, + "actual_response_model": { + "name": "actual_response_model", + "type": "varchar(128)", + "primaryKey": false, + "notNull": false + }, + "endpoint": { + "name": "endpoint", + "type": "varchar(256)", + "primaryKey": false, + "notNull": false + }, + "api_type": { + "name": "api_type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "session_id": { + "name": "session_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "status_code": { + "name": "status_code", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "is_success": { + "name": "is_success", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "success_rate_outcome": { + "name": "success_rate_outcome", + "type": "varchar(16)", + "primaryKey": false, + "notNull": false + }, + "blocked_by": { + "name": "blocked_by", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "cost_usd": { + "name": "cost_usd", + "type": "numeric(21, 15)", + "primaryKey": false, + "notNull": false, + "default": "'0'" + }, + "cost_multiplier": { + "name": "cost_multiplier", + "type": "numeric(10, 4)", + "primaryKey": false, + "notNull": false + }, + "group_cost_multiplier": { + "name": "group_cost_multiplier", + "type": "numeric(10, 4)", + "primaryKey": false, + "notNull": false + }, + "input_tokens": { + "name": "input_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "output_tokens": { + "name": "output_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "cache_creation_input_tokens": { + "name": "cache_creation_input_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "cache_read_input_tokens": { + "name": "cache_read_input_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "cache_creation_5m_input_tokens": { + "name": "cache_creation_5m_input_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "cache_creation_1h_input_tokens": { + "name": "cache_creation_1h_input_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "cache_ttl_applied": { + "name": "cache_ttl_applied", + "type": "varchar(10)", + "primaryKey": false, + "notNull": false + }, + "context_1m_applied": { + "name": "context_1m_applied", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "swap_cache_ttl_applied": { + "name": "swap_cache_ttl_applied", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "duration_ms": { + "name": "duration_ms", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "ttfb_ms": { + "name": "ttfb_ms", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "client_ip": { + "name": "client_ip", + "type": "varchar(45)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "idx_usage_ledger_request_id": { + "name": "idx_usage_ledger_request_id", + "columns": [ + { + "expression": "request_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_usage_ledger_user_created_at": { + "name": "idx_usage_ledger_user_created_at", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"usage_ledger\".\"blocked_by\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_usage_ledger_key_created_at": { + "name": "idx_usage_ledger_key_created_at", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"usage_ledger\".\"blocked_by\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_usage_ledger_provider_created_at": { + "name": "idx_usage_ledger_provider_created_at", + "columns": [ + { + "expression": "final_provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"usage_ledger\".\"blocked_by\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_usage_ledger_created_at_minute": { + "name": "idx_usage_ledger_created_at_minute", + "columns": [ + { + "expression": "date_trunc('minute', \"created_at\" AT TIME ZONE 'UTC')", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_usage_ledger_created_at_desc_id": { + "name": "idx_usage_ledger_created_at_desc_id", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_usage_ledger_session_id": { + "name": "idx_usage_ledger_session_id", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"usage_ledger\".\"session_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_usage_ledger_model": { + "name": "idx_usage_ledger_model", + "columns": [ + { + "expression": "model", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"usage_ledger\".\"model\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_usage_ledger_key_cost": { + "name": "idx_usage_ledger_key_cost", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "cost_usd", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "endpoint", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"usage_ledger\".\"blocked_by\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_usage_ledger_user_cost_cover": { + "name": "idx_usage_ledger_user_cost_cover", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "cost_usd", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "endpoint", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"usage_ledger\".\"blocked_by\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_usage_ledger_provider_cost_cover": { + "name": "idx_usage_ledger_provider_cost_cover", + "columns": [ + { + "expression": "final_provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "cost_usd", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "endpoint", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"usage_ledger\".\"blocked_by\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_usage_ledger_key_created_at_desc_cover": { + "name": "idx_usage_ledger_key_created_at_desc_cover", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "\"created_at\" DESC NULLS LAST", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "final_provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"usage_ledger\".\"blocked_by\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "role": { + "name": "role", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'user'" + }, + "rpm_limit": { + "name": "rpm_limit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "daily_limit_usd": { + "name": "daily_limit_usd", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "provider_group": { + "name": "provider_group", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false, + "default": "'default'" + }, + "tags": { + "name": "tags", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'[]'::jsonb" + }, + "limit_5h_usd": { + "name": "limit_5h_usd", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "limit_5h_reset_mode": { + "name": "limit_5h_reset_mode", + "type": "daily_reset_mode", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'rolling'" + }, + "limit_weekly_usd": { + "name": "limit_weekly_usd", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "limit_monthly_usd": { + "name": "limit_monthly_usd", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "limit_total_usd": { + "name": "limit_total_usd", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "cost_reset_at": { + "name": "cost_reset_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "limit_5h_cost_reset_at": { + "name": "limit_5h_cost_reset_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "limit_concurrent_sessions": { + "name": "limit_concurrent_sessions", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "daily_reset_mode": { + "name": "daily_reset_mode", + "type": "daily_reset_mode", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'fixed'" + }, + "daily_reset_time": { + "name": "daily_reset_time", + "type": "varchar(5)", + "primaryKey": false, + "notNull": true, + "default": "'00:00'" + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "allowed_clients": { + "name": "allowed_clients", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'[]'::jsonb" + }, + "allowed_models": { + "name": "allowed_models", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'[]'::jsonb" + }, + "blocked_clients": { + "name": "blocked_clients", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_users_active_role_sort": { + "name": "idx_users_active_role_sort", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "role", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"users\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_users_enabled_expires_at": { + "name": "idx_users_enabled_expires_at", + "columns": [ + { + "expression": "is_enabled", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"users\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_users_tags_gin": { + "name": "idx_users_tags_gin", + "columns": [ + { + "expression": "tags", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"users\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "gin", + "with": {} + }, + "idx_users_created_at": { + "name": "idx_users_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_users_deleted_at": { + "name": "idx_users_deleted_at", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.webhook_targets": { + "name": "webhook_targets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "provider_type": { + "name": "provider_type", + "type": "webhook_provider_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "webhook_url": { + "name": "webhook_url", + "type": "varchar(1024)", + "primaryKey": false, + "notNull": false + }, + "telegram_bot_token": { + "name": "telegram_bot_token", + "type": "varchar(256)", + "primaryKey": false, + "notNull": false + }, + "telegram_chat_id": { + "name": "telegram_chat_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "dingtalk_secret": { + "name": "dingtalk_secret", + "type": "varchar(256)", + "primaryKey": false, + "notNull": false + }, + "custom_template": { + "name": "custom_template", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "custom_headers": { + "name": "custom_headers", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "proxy_url": { + "name": "proxy_url", + "type": "varchar(512)", + "primaryKey": false, + "notNull": false + }, + "proxy_fallback_to_direct": { + "name": "proxy_fallback_to_direct", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "last_test_at": { + "name": "last_test_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_test_result": { + "name": "last_test_result", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.daily_reset_mode": { + "name": "daily_reset_mode", + "schema": "public", + "values": [ + "fixed", + "rolling" + ] + }, + "public.notification_type": { + "name": "notification_type", + "schema": "public", + "values": [ + "circuit_breaker", + "daily_leaderboard", + "cost_alert", + "cache_hit_rate_alert" + ] + }, + "public.webhook_provider_type": { + "name": "webhook_provider_type", + "schema": "public", + "values": [ + "wechat", + "feishu", + "dingtalk", + "telegram", + "custom" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index 7d08f0bbd..4d57a874a 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -736,6 +736,13 @@ "when": 1780575810214, "tag": "0104_watery_thunderbird", "breakpoints": true + }, + { + "idx": 105, + "version": "7", + "when": 1781156586163, + "tag": "0105_chief_rocket_racer", + "breakpoints": true } ] } \ No newline at end of file diff --git a/messages/en/settings/config.json b/messages/en/settings/config.json index 6d1447d2a..a61789449 100644 --- a/messages/en/settings/config.json +++ b/messages/en/settings/config.json @@ -66,6 +66,8 @@ "enableThinkingSignatureRectifierDesc": "When Anthropic providers return thinking signature incompatibility or invalid request errors, automatically removes incompatible thinking blocks and retries once against the same provider (enabled by default).", "enableThinkingBudgetRectifier": "Enable Thinking Budget Rectifier", "enableThinkingBudgetRectifierDesc": "When Anthropic providers return budget_tokens < 1024 errors, automatically sets thinking budget to maximum (32000) and max_tokens to 64000 if needed, then retries once (enabled by default).", + "enableThinkingEffortConflictRectifier": "Enable Thinking Effort Conflict Rectifier", + "enableThinkingEffortConflictRectifierDesc": "When Anthropic-compatible providers (such as DeepSeek or MiMo) return 400 errors because disabled thinking conflicts with reasoning_effort, automatically strips the effort fields (output_config/reasoning_effort) and retries once against the same provider (enabled by default).", "enableBillingHeaderRectifier": "Enable Billing Header Rectifier", "enableBillingHeaderRectifierDesc": "Proactively removes x-anthropic-billing-header text blocks injected by Claude Code client into the system prompt, preventing Amazon Bedrock and other non-native Anthropic upstreams from returning 400 errors (enabled by default).", "enableResponseInputRectifier": "Enable Response Input Rectifier", diff --git a/messages/ja/settings/config.json b/messages/ja/settings/config.json index 68a55077c..6623f9504 100644 --- a/messages/ja/settings/config.json +++ b/messages/ja/settings/config.json @@ -66,6 +66,8 @@ "enableThinkingSignatureRectifierDesc": "Anthropic プロバイダーで thinking 署名の不整合や不正なリクエストエラーが発生した場合、thinking 関連ブロックを削除して同一プロバイダーへ1回だけ再試行します(既定で有効)。", "enableThinkingBudgetRectifier": "thinking 予算整流を有効化", "enableThinkingBudgetRectifierDesc": "Anthropic プロバイダーで budget_tokens < 1024 エラーが発生した場合、thinking 予算を最大値(32000)に設定し、必要に応じて max_tokens を 64000 に設定して1回だけ再試行します(既定で有効)。", + "enableThinkingEffortConflictRectifier": "thinking effort 競合整流を有効化", + "enableThinkingEffortConflictRectifierDesc": "Anthropic 互換プロバイダー(DeepSeek や MiMo など)が thinking 無効と reasoning_effort の併存により 400 エラーを返した場合、effort フィールド(output_config/reasoning_effort)を自動的に取り除き、同じプロバイダーに対して1回だけ再試行します(既定で有効)。", "enableBillingHeaderRectifier": "課金ヘッダー整流を有効化", "enableBillingHeaderRectifierDesc": "Claude Code クライアントが system プロンプトに注入する x-anthropic-billing-header テキストブロックを事前に削除し、Amazon Bedrock などの非ネイティブ Anthropic 上流による 400 エラーを防止します(既定で有効)。", "enableResponseInputRectifier": "Response Input 整流器を有効化", diff --git a/messages/ru/settings/config.json b/messages/ru/settings/config.json index 52090a13a..c7add884d 100644 --- a/messages/ru/settings/config.json +++ b/messages/ru/settings/config.json @@ -66,6 +66,8 @@ "enableThinkingSignatureRectifierDesc": "Если Anthropic-провайдер возвращает ошибку несовместимой подписи thinking или некорректного запроса, автоматически удаляет несовместимые thinking-блоки и повторяет запрос один раз к тому же провайдеру (включено по умолчанию).", "enableThinkingBudgetRectifier": "Включить исправление thinking-budget", "enableThinkingBudgetRectifierDesc": "Если Anthropic-провайдер возвращает ошибку budget_tokens < 1024, автоматически устанавливает thinking budget на максимум (32000) и при необходимости max_tokens на 64000, затем повторяет запрос один раз (включено по умолчанию).", + "enableThinkingEffortConflictRectifier": "Включить исправление конфликта thinking-effort", + "enableThinkingEffortConflictRectifierDesc": "Если Anthropic-совместимый провайдер (например, DeepSeek или MiMo) возвращает ошибку 400 из-за сочетания отключённого thinking и reasoning_effort, автоматически удаляет поля effort (output_config/reasoning_effort) и повторяет запрос к тому же провайдеру один раз (включено по умолчанию).", "enableBillingHeaderRectifier": "Включить исправление billing-заголовка", "enableBillingHeaderRectifierDesc": "Проактивно удаляет текстовые блоки x-anthropic-billing-header, добавленные клиентом Claude Code в системный промпт, предотвращая ошибки 400 от Amazon Bedrock и других не-Anthropic провайдеров (включено по умолчанию).", "enableResponseInputRectifier": "Включить исправление Response Input", diff --git a/messages/zh-CN/settings/config.json b/messages/zh-CN/settings/config.json index ecd85ddbd..c705683a0 100644 --- a/messages/zh-CN/settings/config.json +++ b/messages/zh-CN/settings/config.json @@ -64,6 +64,8 @@ "enableThinkingSignatureRectifierDesc": "当 Anthropic 类型供应商返回 thinking 签名不兼容或非法请求等错误时,自动移除不兼容的 thinking 相关块并对同一供应商重试一次(默认开启)。", "enableThinkingBudgetRectifier": "启用 thinking 预算整流器", "enableThinkingBudgetRectifierDesc": "当 Anthropic 类型供应商返回 budget_tokens < 1024 错误时,自动将 thinking 预算设为最大值(32000),并在需要时将 max_tokens 设为 64000,然后重试一次(默认开启)。", + "enableThinkingEffortConflictRectifier": "启用 thinking effort 冲突整流器", + "enableThinkingEffortConflictRectifierDesc": "当 Anthropic 兼容供应商(如 DeepSeek、MiMo 等)因 thinking 关闭与 reasoning_effort 同时存在返回 400 错误时,自动剥离 effort 字段(output_config/reasoning_effort)并对同一供应商重试一次(默认开启)。", "enableBillingHeaderRectifier": "启用计费标头整流器", "enableBillingHeaderRectifierDesc": "主动移除 Claude Code 客户端注入到 system 提示中的 x-anthropic-billing-header 文本块,防止 Amazon Bedrock 等非原生 Anthropic 上游返回 400 错误(默认开启)。", "enableResponseInputRectifier": "启用 Response Input 整流器", diff --git a/messages/zh-TW/settings/config.json b/messages/zh-TW/settings/config.json index 7c94a809d..8ef145873 100644 --- a/messages/zh-TW/settings/config.json +++ b/messages/zh-TW/settings/config.json @@ -66,6 +66,8 @@ "enableThinkingSignatureRectifierDesc": "當 Anthropic 類型供應商返回 thinking 簽名不相容或非法請求等錯誤時,自動移除不相容的 thinking 相關區塊並對同一供應商重試一次(預設開啟)。", "enableThinkingBudgetRectifier": "啟用 thinking 預算整流器", "enableThinkingBudgetRectifierDesc": "當 Anthropic 類型供應商返回 budget_tokens < 1024 錯誤時,自動將 thinking 預算設為最大值(32000),並在需要時將 max_tokens 設為 64000,然後重試一次(預設開啟)。", + "enableThinkingEffortConflictRectifier": "啟用 thinking effort 衝突整流器", + "enableThinkingEffortConflictRectifierDesc": "當 Anthropic 相容供應商(如 DeepSeek、MiMo 等)因 thinking 關閉與 reasoning_effort 同時存在返回 400 錯誤時,自動剝離 effort 欄位(output_config/reasoning_effort)並對同一供應商重試一次(預設開啟)。", "enableBillingHeaderRectifier": "啟用計費標頭整流器", "enableBillingHeaderRectifierDesc": "主動移除 Claude Code 客戶端注入到 system 提示中的 x-anthropic-billing-header 文字區塊,防止 Amazon Bedrock 等非原生 Anthropic 上游回傳 400 錯誤(預設開啟)。", "enableResponseInputRectifier": "啟用 Response Input 整流器", diff --git a/package.json b/package.json index 530014dd0..da5ce414b 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "test:coverage:logs-sessionid-time-filter": "vitest run --config tests/configs/logs-sessionid-time-filter.config.ts --coverage", "test:coverage:codex-session-id-completer": "vitest run --config tests/configs/codex-session-id-completer.config.ts --coverage", "test:coverage:thinking-signature-rectifier": "vitest run --config tests/configs/thinking-signature-rectifier.config.ts --coverage", + "test:coverage:thinking-effort-conflict-rectifier": "vitest run --config tests/configs/thinking-effort-conflict-rectifier.config.ts --coverage", "test:coverage:quota": "vitest run --config tests/configs/quota.config.ts --coverage", "test:coverage:my-usage": "vitest run --config tests/configs/my-usage.config.ts --coverage", "test:coverage:proxy-guard-pipeline": "vitest run --config tests/configs/proxy-guard-pipeline.config.ts --coverage", diff --git a/src/actions/system-config.ts b/src/actions/system-config.ts index eb4a24636..c40a7302d 100644 --- a/src/actions/system-config.ts +++ b/src/actions/system-config.ts @@ -73,6 +73,7 @@ export async function saveSystemSettings(formData: { interceptAnthropicWarmupRequests?: boolean; enableThinkingSignatureRectifier?: boolean; enableThinkingBudgetRectifier?: boolean; + enableThinkingEffortConflictRectifier?: boolean; enableBillingHeaderRectifier?: boolean; enableResponseInputRectifier?: boolean; allowNonConversationEndpointProviderFallback?: boolean; @@ -125,6 +126,7 @@ export async function saveSystemSettings(formData: { interceptAnthropicWarmupRequests: validated.interceptAnthropicWarmupRequests, enableThinkingSignatureRectifier: validated.enableThinkingSignatureRectifier, enableThinkingBudgetRectifier: validated.enableThinkingBudgetRectifier, + enableThinkingEffortConflictRectifier: validated.enableThinkingEffortConflictRectifier, enableBillingHeaderRectifier: validated.enableBillingHeaderRectifier, enableResponseInputRectifier: validated.enableResponseInputRectifier, allowNonConversationEndpointProviderFallback: diff --git a/src/app/[locale]/settings/config/_components/system-settings-form.tsx b/src/app/[locale]/settings/config/_components/system-settings-form.tsx index 843ef0338..a6808393e 100644 --- a/src/app/[locale]/settings/config/_components/system-settings-form.tsx +++ b/src/app/[locale]/settings/config/_components/system-settings-form.tsx @@ -76,6 +76,7 @@ interface SystemSettingsFormProps { | "enableBillingHeaderRectifier" | "enableResponseInputRectifier" | "enableThinkingBudgetRectifier" + | "enableThinkingEffortConflictRectifier" | "allowNonConversationEndpointProviderFallback" | "fakeStreamingWhitelist" | "enableCodexSessionIdCompletion" @@ -169,6 +170,8 @@ export function SystemSettingsForm({ initialSettings }: SystemSettingsFormProps) const [enableThinkingBudgetRectifier, setEnableThinkingBudgetRectifier] = useState( initialSettings.enableThinkingBudgetRectifier ); + const [enableThinkingEffortConflictRectifier, setEnableThinkingEffortConflictRectifier] = + useState(initialSettings.enableThinkingEffortConflictRectifier); const [enableCodexSessionIdCompletion, setEnableCodexSessionIdCompletion] = useState( initialSettings.enableCodexSessionIdCompletion ); @@ -317,6 +320,7 @@ export function SystemSettingsForm({ initialSettings }: SystemSettingsFormProps) allowNonConversationEndpointProviderFallback, fakeStreamingWhitelist: sanitizedFakeStreamingWhitelist, enableThinkingBudgetRectifier, + enableThinkingEffortConflictRectifier, enableCodexSessionIdCompletion, enableClaudeMetadataUserIdInjection, enableResponseFixer, @@ -364,6 +368,7 @@ export function SystemSettingsForm({ initialSettings }: SystemSettingsFormProps) })) ); setEnableThinkingBudgetRectifier(result.data.enableThinkingBudgetRectifier); + setEnableThinkingEffortConflictRectifier(result.data.enableThinkingEffortConflictRectifier); setEnableCodexSessionIdCompletion(result.data.enableCodexSessionIdCompletion); setEnableClaudeMetadataUserIdInjection(result.data.enableClaudeMetadataUserIdInjection); setEnableResponseFixer(result.data.enableResponseFixer); @@ -796,6 +801,29 @@ export function SystemSettingsForm({ initialSettings }: SystemSettingsFormProps) /> + {/* Enable Thinking Effort Conflict Rectifier */} +
+
+
+ +
+
+

+ {t("enableThinkingEffortConflictRectifier")} +

+

+ {t("enableThinkingEffortConflictRectifierDesc")} +

+
+
+ setEnableThinkingEffortConflictRectifier(checked)} + disabled={isPending} + /> +
+ {/* Enable Billing Header Rectifier */}
diff --git a/src/app/[locale]/settings/config/page.tsx b/src/app/[locale]/settings/config/page.tsx index 46b992bd0..72cc51a6d 100644 --- a/src/app/[locale]/settings/config/page.tsx +++ b/src/app/[locale]/settings/config/page.tsx @@ -61,6 +61,7 @@ async function SettingsConfigContent({ locale }: { locale: string }) { interceptAnthropicWarmupRequests: settings.interceptAnthropicWarmupRequests, enableThinkingSignatureRectifier: settings.enableThinkingSignatureRectifier, enableThinkingBudgetRectifier: settings.enableThinkingBudgetRectifier, + enableThinkingEffortConflictRectifier: settings.enableThinkingEffortConflictRectifier, enableBillingHeaderRectifier: settings.enableBillingHeaderRectifier, enableResponseInputRectifier: settings.enableResponseInputRectifier, allowNonConversationEndpointProviderFallback: diff --git a/src/app/v1/_lib/proxy/forwarder.ts b/src/app/v1/_lib/proxy/forwarder.ts index a568f0717..523f63a0f 100644 --- a/src/app/v1/_lib/proxy/forwarder.ts +++ b/src/app/v1/_lib/proxy/forwarder.ts @@ -96,6 +96,10 @@ import { detectThinkingBudgetRectifierTrigger, rectifyThinkingBudget, } from "./thinking-budget-rectifier"; +import { + detectThinkingEffortConflictRectifierTrigger, + rectifyThinkingEffortConflict, +} from "./thinking-effort-conflict-rectifier"; import { detectThinkingSignatureRectifierTrigger, rectifyAnthropicRequestMessage, @@ -225,21 +229,27 @@ type StreamingHedgeAttempt = { type ReactiveRectifierRetryState = { thinkingSignatureRetried: boolean; thinkingBudgetRetried: boolean; + thinkingEffortConflictRetried: boolean; }; +type ReactiveRectifierType = + | "thinking_signature_rectifier" + | "thinking_budget_rectifier" + | "thinking_effort_conflict_rectifier"; + type ReactiveRectifierResult = | { matched: false } | { matched: true; applied: false; reason: "already_retried" | "not_applicable"; - rectifierType: "thinking_signature_rectifier" | "thinking_budget_rectifier"; + rectifierType: ReactiveRectifierType; trigger: string; } | { matched: true; applied: true; - rectifierType: "thinking_signature_rectifier" | "thinking_budget_rectifier"; + rectifierType: ReactiveRectifierType; trigger: string; requestDetailsBeforeRectify: ReturnType; }; @@ -762,12 +772,15 @@ function buildRetryFailedChainEntry( }; } -function getReactiveRectifierDisplayName( - rectifierType: "thinking_signature_rectifier" | "thinking_budget_rectifier" -): string { - return rectifierType === "thinking_signature_rectifier" - ? "Thinking signature rectifier" - : "Thinking budget rectifier"; +function getReactiveRectifierDisplayName(rectifierType: ReactiveRectifierType): string { + switch (rectifierType) { + case "thinking_signature_rectifier": + return "Thinking signature rectifier"; + case "thinking_budget_rectifier": + return "Thinking budget rectifier"; + case "thinking_effort_conflict_rectifier": + return "Thinking effort conflict rectifier"; + } } async function tryApplyReactiveAnthropicRectifier(params: { @@ -794,6 +807,68 @@ async function tryApplyReactiveAnthropicRectifier(params: { return { matched: false }; } + // 先于签名整流器检测:effort 冲突的错误文案更具体(reasoning_effort/output_config), + // 避免被签名整流器的通用 invalid request 兜底吞掉。 + const effortConflictTrigger = detectThinkingEffortConflictRectifierTrigger(errorMessage); + if (effortConflictTrigger) { + const settings = await getCachedSystemSettings(); + const enabled = settings.enableThinkingEffortConflictRectifier ?? true; + + if (!enabled) { + return { matched: false }; + } + + if (params.retryState.thinkingEffortConflictRetried) { + return { + matched: true, + applied: false, + reason: "already_retried", + rectifierType: "thinking_effort_conflict_rectifier", + trigger: effortConflictTrigger, + }; + } + + const requestDetailsBeforeRectify = buildRequestDetails(requestSession); + const rectified = rectifyThinkingEffortConflict( + requestSession.request.message as Record + ); + + addSpecialSettingForPersistence(requestSession, persistSession, { + type: "thinking_effort_conflict_rectifier", + scope: "request", + hit: rectified.applied, + providerId: provider.id, + providerName: provider.name, + trigger: effortConflictTrigger, + attemptNumber, + retryAttemptNumber, + removedOutputConfig: rectified.removedOutputConfig, + removedReasoningEffort: rectified.removedReasoningEffort, + thinkingType: rectified.thinkingType, + effort: rectified.effort, + }); + await persistSpecialSettings(persistSession); + + if (!rectified.applied) { + return { + matched: true, + applied: false, + reason: "not_applicable", + rectifierType: "thinking_effort_conflict_rectifier", + trigger: effortConflictTrigger, + }; + } + + params.retryState.thinkingEffortConflictRetried = true; + return { + matched: true, + applied: true, + rectifierType: "thinking_effort_conflict_rectifier", + trigger: effortConflictTrigger, + requestDetailsBeforeRectify, + }; + } + const signatureTrigger = detectThinkingSignatureRectifierTrigger(errorMessage); if (signatureTrigger) { const settings = await getCachedSystemSettings(); @@ -1094,6 +1169,7 @@ export class ProxyForwarder { const reactiveRectifierRetryState: ReactiveRectifierRetryState = { thinkingSignatureRetried: false, thinkingBudgetRetried: false, + thinkingEffortConflictRetried: false, }; const requestPath = session.requestUrl.pathname; @@ -4452,6 +4528,7 @@ export class ProxyForwarder { reactiveRectifierRetryState: { thinkingSignatureRetried: false, thinkingBudgetRetried: false, + thinkingEffortConflictRetried: false, }, settled: false, thresholdTriggered: false, diff --git a/src/app/v1/_lib/proxy/thinking-effort-conflict-rectifier.test.ts b/src/app/v1/_lib/proxy/thinking-effort-conflict-rectifier.test.ts new file mode 100644 index 000000000..db01dad57 --- /dev/null +++ b/src/app/v1/_lib/proxy/thinking-effort-conflict-rectifier.test.ts @@ -0,0 +1,186 @@ +import { describe, expect, test } from "vitest"; +import { + detectThinkingEffortConflictRectifierTrigger, + rectifyThinkingEffortConflict, +} from "./thinking-effort-conflict-rectifier"; + +describe("detectThinkingEffortConflictRectifierTrigger", () => { + test("matches the documented DeepSeek conflict error", () => { + expect( + detectThinkingEffortConflictRectifierTrigger( + "thinking options type cannot be disabled when reasoning_effort is set" + ) + ).toBe("thinking_disabled_with_reasoning_effort"); + }); + + test("matches the error embedded in a proxy upstream envelope", () => { + expect( + detectThinkingEffortConflictRectifierTrigger( + 'Provider deepseek returned 400: Provider returned 400: Bad Request | Upstream: {"error":{"message":"thinking options type cannot be disabled when reasoning_effort is set","type":"invalid_request_error","param":null,"code":"invalid_request_error"}}' + ) + ).toBe("thinking_disabled_with_reasoning_effort"); + }); + + test("matches case and quoting variants", () => { + expect( + detectThinkingEffortConflictRectifierTrigger( + "Thinking options `type` cannot be disabled when `reasoning_effort` is set." + ) + ).toBe("thinking_disabled_with_reasoning_effort"); + }); + + test("matches output_config flavored variants", () => { + expect( + detectThinkingEffortConflictRectifierTrigger( + "thinking cannot be disabled when output_config.effort is set" + ) + ).toBe("thinking_disabled_with_reasoning_effort"); + }); + + test("ignores unrelated errors", () => { + expect(detectThinkingEffortConflictRectifierTrigger(null)).toBeNull(); + expect(detectThinkingEffortConflictRectifierTrigger(undefined)).toBeNull(); + expect(detectThinkingEffortConflictRectifierTrigger("")).toBeNull(); + expect( + detectThinkingEffortConflictRectifierTrigger("Invalid `signature` in `thinking` block") + ).toBeNull(); + expect( + detectThinkingEffortConflictRectifierTrigger( + "thinking.enabled.budget_tokens: Input should be greater than or equal to 1024" + ) + ).toBeNull(); + expect( + detectThinkingEffortConflictRectifierTrigger("reasoning_effort must be one of low|medium") + ).toBeNull(); + expect(detectThinkingEffortConflictRectifierTrigger("invalid request: malformed")).toBeNull(); + }); +}); + +describe("rectifyThinkingEffortConflict", () => { + test("removes output_config when thinking is disabled (Claude Code subagent shape)", () => { + const message: Record = { + model: "deepseek-v4-pro", + thinking: { type: "disabled" }, + output_config: { effort: "max" }, + messages: [{ role: "user", content: [{ type: "text", text: "hi" }] }], + }; + + const result = rectifyThinkingEffortConflict(message); + + expect(result.applied).toBe(true); + expect(result.removedOutputConfig).toBe(true); + expect(result.removedReasoningEffort).toBe(false); + expect(result.thinkingType).toBe("disabled"); + expect(result.effort).toBe("max"); + expect("output_config" in message).toBe(false); + expect(message.thinking).toEqual({ type: "disabled" }); + }); + + test("strips only effort and preserves sibling keys in output_config", () => { + const message: Record = { + thinking: { type: "disabled" }, + output_config: { effort: "max", verbosity: "high", future_flag: true }, + messages: [], + }; + + const result = rectifyThinkingEffortConflict(message); + + expect(result.applied).toBe(true); + expect(result.removedOutputConfig).toBe(true); + expect(result.effort).toBe("max"); + // Sibling fields must survive; only the conflicting effort carrier is removed. + expect(message.output_config).toEqual({ verbosity: "high", future_flag: true }); + }); + + test("drops output_config entirely when effort was its only key", () => { + const message: Record = { + thinking: { type: "disabled" }, + output_config: { effort: "max" }, + messages: [], + }; + + rectifyThinkingEffortConflict(message); + + expect("output_config" in message).toBe(false); + }); + + test("removes a top-level reasoning_effort passthrough as well", () => { + const message: Record = { + thinking: { type: "disabled" }, + reasoning_effort: "high", + messages: [], + }; + + const result = rectifyThinkingEffortConflict(message); + + expect(result.applied).toBe(true); + expect(result.removedOutputConfig).toBe(false); + expect(result.removedReasoningEffort).toBe(true); + expect(result.effort).toBe("high"); + expect("reasoning_effort" in message).toBe(false); + }); + + test("treats a missing thinking field as disabled and strips effort", () => { + const message: Record = { + output_config: { effort: "medium" }, + messages: [], + }; + + const result = rectifyThinkingEffortConflict(message); + + expect(result.applied).toBe(true); + expect(result.removedOutputConfig).toBe(true); + expect(result.thinkingType).toBeNull(); + }); + + test("does not touch requests with thinking enabled", () => { + const message: Record = { + thinking: { type: "enabled", budget_tokens: 2048 }, + output_config: { effort: "max" }, + }; + + const result = rectifyThinkingEffortConflict(message); + + expect(result.applied).toBe(false); + expect(message.output_config).toEqual({ effort: "max" }); + expect(message.thinking).toEqual({ type: "enabled", budget_tokens: 2048 }); + }); + + test("does not touch requests with adaptive thinking", () => { + const message: Record = { + thinking: { type: "adaptive" }, + output_config: { effort: "low" }, + }; + + expect(rectifyThinkingEffortConflict(message).applied).toBe(false); + expect(message.output_config).toEqual({ effort: "low" }); + }); + + test("is a no-op when no effort fields are present", () => { + const message: Record = { + thinking: { type: "disabled" }, + messages: [], + }; + + const result = rectifyThinkingEffortConflict(message); + + expect(result.applied).toBe(false); + expect(result.removedOutputConfig).toBe(false); + expect(result.removedReasoningEffort).toBe(false); + }); + + test("keeps an effort-less output_config in place", () => { + const message: Record = { + thinking: { type: "disabled" }, + output_config: { something_else: true }, + reasoning_effort: "low", + }; + + const result = rectifyThinkingEffortConflict(message); + + expect(result.applied).toBe(true); + expect(result.removedOutputConfig).toBe(false); + expect(result.removedReasoningEffort).toBe(true); + expect(message.output_config).toEqual({ something_else: true }); + }); +}); diff --git a/src/app/v1/_lib/proxy/thinking-effort-conflict-rectifier.ts b/src/app/v1/_lib/proxy/thinking-effort-conflict-rectifier.ts new file mode 100644 index 000000000..a05110c73 --- /dev/null +++ b/src/app/v1/_lib/proxy/thinking-effort-conflict-rectifier.ts @@ -0,0 +1,119 @@ +/** + * Thinking Effort Conflict Rectifier - Reactive rectifier for strict Anthropic-compatible + * providers (DeepSeek, MiMo, ...) that reject `thinking: { type: "disabled" }` combined + * with a reasoning-effort field. + * + * Background (issue #1257): Claude Code v2.1.166+ disables thinking for subagent tasks + * but keeps the global `output_config: { effort }` in the payload. The official Anthropic + * API ignores the contradiction; DeepSeek's validation rejects it with + * "thinking options type cannot be disabled when reasoning_effort is set" (DeepSeek docs: + * `output_config` only supports `effort`, which maps to reasoning_effort internally). + * + * Action: strip the effort fields (`output_config.effort` carrier / top-level + * `reasoning_effort`) while keeping thinking disabled, then retry the same provider once. + */ + +export type ThinkingEffortConflictRectifierTrigger = "thinking_disabled_with_reasoning_effort"; + +export type ThinkingEffortConflictRectifierResult = { + applied: boolean; + removedOutputConfig: boolean; + removedReasoningEffort: boolean; + thinkingType: string | null; + effort: string | null; +}; + +/** + * 检测是否需要触发「thinking effort 冲突整流器」 + * + * 注意:不依赖错误规则开关(error rules 可能被用户关闭),仅做字符串匹配。 + */ +export function detectThinkingEffortConflictRectifierTrigger( + errorMessage: string | null | undefined +): ThinkingEffortConflictRectifierTrigger | null { + if (!errorMessage) return null; + + const lower = errorMessage.toLowerCase(); + + const mentionsDisableConflict = + lower.includes("cannot be disabled") || lower.includes("can not be disabled"); + if (!mentionsDisableConflict) return null; + + // DeepSeek 原文:thinking options type cannot be disabled when reasoning_effort is set + if (lower.includes("reasoning_effort")) { + return "thinking_disabled_with_reasoning_effort"; + } + + // 变体兜底:以 output_config(.effort) 表述同一冲突的上游 + if (lower.includes("output_config") && lower.includes("thinking")) { + return "thinking_disabled_with_reasoning_effort"; + } + + return null; +} + +/** + * 对 Anthropic 请求体做最小侵入整流: + * - 仅当 thinking 关闭(或缺省,上游按关闭处理)时生效 + * - 移除携带 effort 的 output_config 与顶层 reasoning_effort 透传字段 + * - 保留 thinking 关闭状态(尊重客户端对子 agent 关闭思考的意图) + * + * 说明:仅在上游报错后、同供应商重试前调用(被动触发),不影响正常请求; + * 原地修改 message 对象。 + */ +export function rectifyThinkingEffortConflict( + message: Record +): ThinkingEffortConflictRectifierResult { + const thinking = message.thinking; + const thinkingType = + thinking && typeof thinking === "object" && !Array.isArray(thinking) + ? typeof (thinking as Record).type === "string" + ? ((thinking as Record).type as string) + : null + : null; + + const result: ThinkingEffortConflictRectifierResult = { + applied: false, + removedOutputConfig: false, + removedReasoningEffort: false, + thinkingType, + effort: null, + }; + + // thinking 显式启用(enabled/adaptive 等)时不属于该冲突,保持原样 + const thinkingDisabled = thinkingType === null || thinkingType === "disabled"; + if (!thinkingDisabled) { + return result; + } + + const outputConfig = message.output_config; + const outputConfigEffort = + outputConfig && typeof outputConfig === "object" && !Array.isArray(outputConfig) + ? (outputConfig as Record).effort + : undefined; + + if (outputConfigEffort !== undefined) { + result.effort = typeof outputConfigEffort === "string" ? outputConfigEffort : null; + // 仅剥离冲突的 effort 字段,保留 output_config 中的其他配置;若剥离后为空对象则整体移除。 + const { effort: _removedEffort, ...restOutputConfig } = outputConfig as Record; + if (Object.keys(restOutputConfig).length > 0) { + message.output_config = restOutputConfig; + } else { + delete message.output_config; + } + result.removedOutputConfig = true; + result.applied = true; + } + + const reasoningEffort = message.reasoning_effort; + if (reasoningEffort !== undefined) { + if (result.effort === null && typeof reasoningEffort === "string") { + result.effort = reasoningEffort; + } + delete message.reasoning_effort; + result.removedReasoningEffort = true; + result.applied = true; + } + + return result; +} diff --git a/src/drizzle/schema.ts b/src/drizzle/schema.ts index 034c07a1f..ff1a76a2a 100644 --- a/src/drizzle/schema.ts +++ b/src/drizzle/schema.ts @@ -804,6 +804,13 @@ export const systemSettings = pgTable('system_settings', { .notNull() .default(true), + // thinking effort 冲突整流器(默认开启) + // 开启后:当 Anthropic 兼容供应商(DeepSeek/MiMo 等)因 thinking 关闭 + reasoning_effort 同时存在 + // 返回 400 错误时,自动剥离 effort 字段并对同供应商重试一次 + enableThinkingEffortConflictRectifier: boolean('enable_thinking_effort_conflict_rectifier') + .notNull() + .default(true), + // billing header 整流器(默认开启) // 开启后:主动移除 Claude Code 客户端注入到 system 提示中的 x-anthropic-billing-header 文本块 enableBillingHeaderRectifier: boolean('enable_billing_header_rectifier') diff --git a/src/lib/api-client/v1/openapi-types.gen.ts b/src/lib/api-client/v1/openapi-types.gen.ts index 3b3aba480..b603e42ec 100644 --- a/src/lib/api-client/v1/openapi-types.gen.ts +++ b/src/lib/api-client/v1/openapi-types.gen.ts @@ -11655,6 +11655,8 @@ export interface operations { enableThinkingSignatureRectifier: boolean; /** @description Whether thinking budget rectifier retries are enabled. */ enableThinkingBudgetRectifier: boolean; + /** @description Whether thinking effort conflict rectifier retries are enabled. */ + enableThinkingEffortConflictRectifier: boolean; /** @description Whether billing-header rectifier is enabled. */ enableBillingHeaderRectifier: boolean; /** @description Whether Responses API input rectifier is enabled. */ @@ -11913,6 +11915,8 @@ export interface operations { enableThinkingSignatureRectifier?: boolean; /** @description Whether thinking budget rectifier retries are enabled. */ enableThinkingBudgetRectifier?: boolean; + /** @description Whether thinking effort conflict rectifier retries are enabled. */ + enableThinkingEffortConflictRectifier?: boolean; /** @description Whether billing-header rectifier is enabled. */ enableBillingHeaderRectifier?: boolean; /** @description Whether Responses API input rectifier is enabled. */ @@ -12044,6 +12048,8 @@ export interface operations { enableThinkingSignatureRectifier: boolean; /** @description Whether thinking budget rectifier retries are enabled. */ enableThinkingBudgetRectifier: boolean; + /** @description Whether thinking effort conflict rectifier retries are enabled. */ + enableThinkingEffortConflictRectifier: boolean; /** @description Whether billing-header rectifier is enabled. */ enableBillingHeaderRectifier: boolean; /** @description Whether Responses API input rectifier is enabled. */ diff --git a/src/lib/api/v1/schemas/system-config.ts b/src/lib/api/v1/schemas/system-config.ts index 92dc06ecb..944699f0c 100644 --- a/src/lib/api/v1/schemas/system-config.ts +++ b/src/lib/api/v1/schemas/system-config.ts @@ -126,6 +126,9 @@ export const SystemSettingsSchema = z enableThinkingBudgetRectifier: z .boolean() .describe("Whether thinking budget rectifier retries are enabled."), + enableThinkingEffortConflictRectifier: z + .boolean() + .describe("Whether thinking effort conflict rectifier retries are enabled."), enableBillingHeaderRectifier: z .boolean() .describe("Whether billing-header rectifier is enabled."), diff --git a/src/lib/config/system-settings-cache.ts b/src/lib/config/system-settings-cache.ts index 232349d56..2b4043460 100644 --- a/src/lib/config/system-settings-cache.ts +++ b/src/lib/config/system-settings-cache.ts @@ -41,6 +41,7 @@ const DEFAULT_SETTINGS: Pick< | "codexPriorityBillingSource" | "enableThinkingSignatureRectifier" | "enableThinkingBudgetRectifier" + | "enableThinkingEffortConflictRectifier" | "enableBillingHeaderRectifier" | "enableResponseInputRectifier" | "allowNonConversationEndpointProviderFallback" @@ -60,6 +61,7 @@ const DEFAULT_SETTINGS: Pick< codexPriorityBillingSource: "requested", enableThinkingSignatureRectifier: true, enableThinkingBudgetRectifier: true, + enableThinkingEffortConflictRectifier: true, enableBillingHeaderRectifier: true, enableResponseInputRectifier: true, // 安全敏感开关:冷缓存 / DB 读取失败时 fail-closed,避免意外重新开启跨供应商 raw fallback。 @@ -148,6 +150,7 @@ export async function getCachedSystemSettings(): Promise { interceptAnthropicWarmupRequests: DEFAULT_SETTINGS.interceptAnthropicWarmupRequests, enableThinkingSignatureRectifier: DEFAULT_SETTINGS.enableThinkingSignatureRectifier, enableThinkingBudgetRectifier: DEFAULT_SETTINGS.enableThinkingBudgetRectifier, + enableThinkingEffortConflictRectifier: DEFAULT_SETTINGS.enableThinkingEffortConflictRectifier, enableBillingHeaderRectifier: DEFAULT_SETTINGS.enableBillingHeaderRectifier, enableResponseInputRectifier: DEFAULT_SETTINGS.enableResponseInputRectifier, allowNonConversationEndpointProviderFallback: diff --git a/src/lib/utils/special-settings.ts b/src/lib/utils/special-settings.ts index a5f31c028..09922e741 100644 --- a/src/lib/utils/special-settings.ts +++ b/src/lib/utils/special-settings.ts @@ -75,6 +75,19 @@ function buildSettingKey(setting: SpecialSetting): string { setting.removedRedactedThinkingBlocks, setting.removedSignatureFields, ]); + case "thinking_effort_conflict_rectifier": + return JSON.stringify([ + setting.type, + setting.hit, + setting.providerId ?? null, + setting.trigger, + setting.attemptNumber, + setting.retryAttemptNumber, + setting.removedOutputConfig, + setting.removedReasoningEffort, + setting.thinkingType, + setting.effort, + ]); case "codex_session_id_completion": return JSON.stringify([ setting.type, diff --git a/src/lib/validation/schemas.ts b/src/lib/validation/schemas.ts index 368f564ab..5640440ff 100644 --- a/src/lib/validation/schemas.ts +++ b/src/lib/validation/schemas.ts @@ -1005,6 +1005,8 @@ export const UpdateSystemSettingsSchema = z.object({ enableThinkingSignatureRectifier: z.boolean().optional(), // thinking budget 整流器(可选) enableThinkingBudgetRectifier: z.boolean().optional(), + // thinking effort 冲突整流器(可选) + enableThinkingEffortConflictRectifier: z.boolean().optional(), // billing header 整流器(可选) enableBillingHeaderRectifier: z.boolean().optional(), // Response API input 整流器(可选) diff --git a/src/repository/_shared/transformers.ts b/src/repository/_shared/transformers.ts index e26969ce0..353b5e5d6 100644 --- a/src/repository/_shared/transformers.ts +++ b/src/repository/_shared/transformers.ts @@ -266,6 +266,8 @@ export function toSystemSettings(dbSettings: any): SystemSettings { interceptAnthropicWarmupRequests: dbSettings?.interceptAnthropicWarmupRequests ?? false, enableThinkingSignatureRectifier: dbSettings?.enableThinkingSignatureRectifier ?? true, enableThinkingBudgetRectifier: dbSettings?.enableThinkingBudgetRectifier ?? true, + enableThinkingEffortConflictRectifier: + dbSettings?.enableThinkingEffortConflictRectifier ?? true, enableBillingHeaderRectifier: dbSettings?.enableBillingHeaderRectifier ?? true, enableResponseInputRectifier: dbSettings?.enableResponseInputRectifier ?? true, allowNonConversationEndpointProviderFallback: diff --git a/src/repository/system-config.ts b/src/repository/system-config.ts index ab83f114d..d3f6262b6 100644 --- a/src/repository/system-config.ts +++ b/src/repository/system-config.ts @@ -163,6 +163,7 @@ function createFallbackSettings(): SystemSettings { interceptAnthropicWarmupRequests: false, enableThinkingSignatureRectifier: true, enableThinkingBudgetRectifier: true, + enableThinkingEffortConflictRectifier: true, enableBillingHeaderRectifier: true, enableResponseInputRectifier: true, allowNonConversationEndpointProviderFallback: true, @@ -218,6 +219,7 @@ export async function getSystemSettings(): Promise { interceptAnthropicWarmupRequests: systemSettings.interceptAnthropicWarmupRequests, enableThinkingSignatureRectifier: systemSettings.enableThinkingSignatureRectifier, enableThinkingBudgetRectifier: systemSettings.enableThinkingBudgetRectifier, + enableThinkingEffortConflictRectifier: systemSettings.enableThinkingEffortConflictRectifier, enableBillingHeaderRectifier: systemSettings.enableBillingHeaderRectifier, enableResponseInputRectifier: systemSettings.enableResponseInputRectifier, allowNonConversationEndpointProviderFallback: @@ -260,6 +262,7 @@ export async function getSystemSettings(): Promise { interceptAnthropicWarmupRequests: systemSettings.interceptAnthropicWarmupRequests, enableThinkingSignatureRectifier: systemSettings.enableThinkingSignatureRectifier, enableThinkingBudgetRectifier: systemSettings.enableThinkingBudgetRectifier, + enableThinkingEffortConflictRectifier: systemSettings.enableThinkingEffortConflictRectifier, enableBillingHeaderRectifier: systemSettings.enableBillingHeaderRectifier, enableResponseInputRectifier: systemSettings.enableResponseInputRectifier, allowNonConversationEndpointProviderFallback: @@ -302,9 +305,33 @@ export async function getSystemSettings(): Promise { error, }); - // 最新降级:移除最近新增的 billHedgeLosers 列。 + // 最新降级:移除最近新增的 enableThinkingEffortConflictRectifier 列。 + const { + enableThinkingEffortConflictRectifier: _omitEffortConflict, + ...selectionWithoutEffortConflict + } = fullSelection; + + try { + const [row] = await db + .select(selectionWithoutEffortConflict) + .from(systemSettings) + .orderBy(asc(systemSettings.id)) + .limit(1); + return row ?? null; + } catch (effortConflictFallbackError) { + if (!isUndefinedColumnError(effortConflictFallbackError)) { + throw effortConflictFallbackError; + } + + logger.warn( + "system_settings 表除 enableThinkingEffortConflictRectifier 外仍有列缺失,继续回退到上一代字段集。", + { error: effortConflictFallbackError } + ); + } + + // 次新降级:移除 billHedgeLosers 列。 const { billHedgeLosers: _omitBillHedgeLosers, ...selectionWithoutBillHedgeLosers } = - fullSelection; + selectionWithoutEffortConflict; try { const [row] = await db @@ -580,6 +607,7 @@ export async function updateSystemSettings( interceptAnthropicWarmupRequests: systemSettings.interceptAnthropicWarmupRequests, enableThinkingSignatureRectifier: systemSettings.enableThinkingSignatureRectifier, enableThinkingBudgetRectifier: systemSettings.enableThinkingBudgetRectifier, + enableThinkingEffortConflictRectifier: systemSettings.enableThinkingEffortConflictRectifier, enableBillingHeaderRectifier: systemSettings.enableBillingHeaderRectifier, enableResponseInputRectifier: systemSettings.enableResponseInputRectifier, allowNonConversationEndpointProviderFallback: @@ -622,6 +650,7 @@ export async function updateSystemSettings( interceptAnthropicWarmupRequests: systemSettings.interceptAnthropicWarmupRequests, enableThinkingSignatureRectifier: systemSettings.enableThinkingSignatureRectifier, enableThinkingBudgetRectifier: systemSettings.enableThinkingBudgetRectifier, + enableThinkingEffortConflictRectifier: systemSettings.enableThinkingEffortConflictRectifier, enableBillingHeaderRectifier: systemSettings.enableBillingHeaderRectifier, enableResponseInputRectifier: systemSettings.enableResponseInputRectifier, enableCodexSessionIdCompletion: systemSettings.enableCodexSessionIdCompletion, @@ -751,6 +780,11 @@ export async function updateSystemSettings( updates.enableThinkingBudgetRectifier = payload.enableThinkingBudgetRectifier; } + // thinking effort 冲突整流器开关(如果提供) + if (payload.enableThinkingEffortConflictRectifier !== undefined) { + updates.enableThinkingEffortConflictRectifier = payload.enableThinkingEffortConflictRectifier; + } + // billing header 整流器开关(如果提供) if (payload.enableBillingHeaderRectifier !== undefined) { updates.enableBillingHeaderRectifier = payload.enableBillingHeaderRectifier; @@ -846,26 +880,57 @@ export async function updateSystemSettings( error, }); - // 最新降级:移除最近新增的 billHedgeLosers 列。 - const { billHedgeLosers: _omitUpdateBillHedgeLosers, ...updatesWithoutBillHedgeLosers } = - updates; - const { billHedgeLosers: _omitReturningBillHedgeLosers, ...returningWithoutBillHedgeLosers } = - fullReturning; + // 最新降级:移除最近新增的 enableThinkingEffortConflictRectifier 列。 + const { + enableThinkingEffortConflictRectifier: _omitUpdateEffortConflict, + ...updatesWithoutEffortConflict + } = updates; + const { + enableThinkingEffortConflictRectifier: _omitReturningEffortConflict, + ...returningWithoutEffortConflict + } = fullReturning; try { [updated] = await executor .update(systemSettings) - .set(updatesWithoutBillHedgeLosers) + .set(updatesWithoutEffortConflict) .where(eq(systemSettings.id, current.id)) - .returning(returningWithoutBillHedgeLosers); - } catch (billHedgeLosersFallbackError) { - if (!isUndefinedColumnError(billHedgeLosersFallbackError)) { - throw billHedgeLosersFallbackError; + .returning(returningWithoutEffortConflict); + } catch (effortConflictFallbackError) { + if (!isUndefinedColumnError(effortConflictFallbackError)) { + throw effortConflictFallbackError; } - logger.warn("system_settings 表除 billHedgeLosers 外仍有列缺失,继续降级更新。", { - error: billHedgeLosersFallbackError, - }); + logger.warn( + "system_settings 表除 enableThinkingEffortConflictRectifier 外仍有列缺失,继续降级更新。", + { + error: effortConflictFallbackError, + } + ); + } + + // 次新降级:移除 billHedgeLosers 列。 + const { billHedgeLosers: _omitUpdateBillHedgeLosers, ...updatesWithoutBillHedgeLosers } = + updatesWithoutEffortConflict; + const { billHedgeLosers: _omitReturningBillHedgeLosers, ...returningWithoutBillHedgeLosers } = + returningWithoutEffortConflict; + + if (!updated) { + try { + [updated] = await executor + .update(systemSettings) + .set(updatesWithoutBillHedgeLosers) + .where(eq(systemSettings.id, current.id)) + .returning(returningWithoutBillHedgeLosers); + } catch (billHedgeLosersFallbackError) { + if (!isUndefinedColumnError(billHedgeLosersFallbackError)) { + throw billHedgeLosersFallbackError; + } + + logger.warn("system_settings 表除 billHedgeLosers 外仍有列缺失,继续降级更新。", { + error: billHedgeLosersFallbackError, + }); + } } // 次新降级:移除 billNonSuccessfulRequests 列。 diff --git a/src/types/special-settings.ts b/src/types/special-settings.ts index a93b8d1fe..c24bc1930 100644 --- a/src/types/special-settings.ts +++ b/src/types/special-settings.ts @@ -11,6 +11,7 @@ export type SpecialSetting = | GuardInterceptSpecialSetting | ThinkingSignatureRectifierSpecialSetting | ThinkingBudgetRectifierSpecialSetting + | ThinkingEffortConflictRectifierSpecialSetting | BillingHeaderRectifierSpecialSetting | CodexSessionIdCompletionSpecialSetting | ClaudeMetadataUserIdInjectionSpecialSetting @@ -148,6 +149,28 @@ export type ThinkingSignatureRectifierSpecialSetting = { removedSignatureFields: number; }; +/** + * Thinking effort 冲突整流器审计 + * + * 用于记录:当 Anthropic 兼容供应商(如 DeepSeek、MiMo 等)因 + * thinking 关闭 + reasoning_effort/output_config.effort 同时存在而返回 400 时, + * 代理剥离 effort 字段并对同供应商自动重试一次的行为。 + */ +export type ThinkingEffortConflictRectifierSpecialSetting = { + type: "thinking_effort_conflict_rectifier"; + scope: "request"; + hit: boolean; + providerId: number | null; + providerName: string | null; + trigger: "thinking_disabled_with_reasoning_effort"; + attemptNumber: number; + retryAttemptNumber: number; + removedOutputConfig: boolean; + removedReasoningEffort: boolean; + thinkingType: string | null; + effort: string | null; +}; + /** * Codex Session ID 补全审计 * diff --git a/src/types/system-config.ts b/src/types/system-config.ts index e0080a37f..d0d168ed7 100644 --- a/src/types/system-config.ts +++ b/src/types/system-config.ts @@ -91,6 +91,11 @@ export interface SystemSettings { // 目标:当 Anthropic 类型供应商出现 budget_tokens < 1024 错误时,自动整流并重试一次 enableThinkingBudgetRectifier: boolean; + // thinking effort 冲突整流器(默认开启) + // 目标:当 Anthropic 兼容供应商(DeepSeek/MiMo 等)因 thinking 关闭 + reasoning_effort + // 同时存在返回 400 错误时,自动剥离 effort 字段并对同供应商重试一次 + enableThinkingEffortConflictRectifier: boolean; + // billing header 整流器(默认开启) // 目标:主动移除 Claude Code 客户端注入到 system 提示中的 x-anthropic-billing-header 文本块, // 防止 Amazon Bedrock 等非原生 Anthropic 上游返回 400 错误 @@ -196,6 +201,9 @@ export interface UpdateSystemSettingsInput { // thinking budget 整流器(可选) enableThinkingBudgetRectifier?: boolean; + // thinking effort 冲突整流器(可选) + enableThinkingEffortConflictRectifier?: boolean; + // billing header 整流器(可选) enableBillingHeaderRectifier?: boolean; diff --git a/tests/configs/thinking-effort-conflict-rectifier.config.ts b/tests/configs/thinking-effort-conflict-rectifier.config.ts new file mode 100644 index 000000000..5585c41c1 --- /dev/null +++ b/tests/configs/thinking-effort-conflict-rectifier.config.ts @@ -0,0 +1,12 @@ +import { createCoverageConfig } from "../vitest.base"; + +export default createCoverageConfig({ + name: "thinking-effort-conflict-rectifier", + environment: "node", + testFiles: [ + "src/app/v1/_lib/proxy/thinking-effort-conflict-rectifier.test.ts", + "tests/unit/proxy/proxy-forwarder-thinking-effort-conflict-rectifier.test.ts", + ], + sourceFiles: ["src/app/v1/_lib/proxy/thinking-effort-conflict-rectifier.ts"], + thresholds: { lines: 80, functions: 80, branches: 70, statements: 80 }, +}); diff --git a/tests/unit/actions/system-config-thinking-effort-conflict-setting.test.ts b/tests/unit/actions/system-config-thinking-effort-conflict-setting.test.ts new file mode 100644 index 000000000..c1f980722 --- /dev/null +++ b/tests/unit/actions/system-config-thinking-effort-conflict-setting.test.ts @@ -0,0 +1,39 @@ +import { describe, expect, test, vi } from "vitest"; + +vi.mock("server-only", () => ({})); + +describe("enableThinkingEffortConflictRectifier system setting", () => { + test("defaults to enabled in the DB-row transformer", async () => { + const { toSystemSettings } = await import("@/repository/_shared/transformers"); + + expect(toSystemSettings(undefined).enableThinkingEffortConflictRectifier).toBe(true); + expect( + toSystemSettings({ id: 1, siteTitle: "Claude Code Hub" }) + .enableThinkingEffortConflictRectifier + ).toBe(true); + expect( + toSystemSettings({ id: 1, enableThinkingEffortConflictRectifier: false }) + .enableThinkingEffortConflictRectifier + ).toBe(false); + }); + + test("is accepted by the settings update validation schema", async () => { + const { UpdateSystemSettingsSchema } = await import("@/lib/validation/schemas"); + + const parsed = UpdateSystemSettingsSchema.parse({ + enableThinkingEffortConflictRectifier: false, + }); + expect(parsed.enableThinkingEffortConflictRectifier).toBe(false); + + const empty = UpdateSystemSettingsSchema.parse({}); + expect(empty.enableThinkingEffortConflictRectifier).toBeUndefined(); + }); + + test("is exposed by the v1 system settings response schema", async () => { + const { SystemSettingsSchema } = await import("@/lib/api/v1/schemas/system-config"); + + expect(Object.keys(SystemSettingsSchema.shape)).toContain( + "enableThinkingEffortConflictRectifier" + ); + }); +}); diff --git a/tests/unit/proxy/proxy-forwarder-thinking-effort-conflict-rectifier.test.ts b/tests/unit/proxy/proxy-forwarder-thinking-effort-conflict-rectifier.test.ts new file mode 100644 index 000000000..ad5bd8171 --- /dev/null +++ b/tests/unit/proxy/proxy-forwarder-thinking-effort-conflict-rectifier.test.ts @@ -0,0 +1,253 @@ +import { beforeEach, describe, expect, test, vi } from "vitest"; + +const mocks = vi.hoisted(() => { + return { + getCachedSystemSettings: vi.fn(async () => ({ + enableThinkingEffortConflictRectifier: true, + enableHighConcurrencyMode: false, + })), + recordSuccess: vi.fn(), + recordFailure: vi.fn(async () => {}), + getCircuitState: vi.fn(() => "closed"), + getProviderHealthInfo: vi.fn(async () => ({ + health: { failureCount: 0 }, + config: { failureThreshold: 3 }, + })), + updateMessageRequestDetails: vi.fn(async () => {}), + storeSessionSpecialSettings: vi.fn(async () => {}), + updateSessionBindingSmart: vi.fn(async () => ({ + updated: true, + reason: "first_success", + details: "mocked", + })), + updateSessionProvider: vi.fn(async () => {}), + }; +}); + +vi.mock("@/lib/config", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + isHttp2Enabled: vi.fn(async () => false), + getCachedSystemSettings: mocks.getCachedSystemSettings, + }; +}); + +vi.mock("@/lib/circuit-breaker", () => ({ + getCircuitState: mocks.getCircuitState, + getProviderHealthInfo: mocks.getProviderHealthInfo, + recordFailure: mocks.recordFailure, + recordSuccess: mocks.recordSuccess, +})); + +vi.mock("@/repository/message", () => ({ + updateMessageRequestDetails: mocks.updateMessageRequestDetails, +})); + +vi.mock("@/lib/session-manager", () => ({ + SessionManager: { + storeSessionSpecialSettings: mocks.storeSessionSpecialSettings, + updateSessionBindingSmart: mocks.updateSessionBindingSmart, + updateSessionProvider: mocks.updateSessionProvider, + }, +})); + +import { ProxyError } from "@/app/v1/_lib/proxy/errors"; +import { ProxyForwarder } from "@/app/v1/_lib/proxy/forwarder"; +import { ProxySession } from "@/app/v1/_lib/proxy/session"; +import type { Provider } from "@/types/provider"; + +const DEEPSEEK_CONFLICT_ERROR = + 'Provider returned 400: Bad Request | Upstream: {"error":{"message":"thinking options type cannot be disabled when reasoning_effort is set","type":"invalid_request_error","param":null,"code":"invalid_request_error"}}'; + +function createSession(): ProxySession { + const headers = new Headers(); + const session = Object.create(ProxySession.prototype); + + Object.assign(session, { + startTime: Date.now(), + method: "POST", + requestUrl: new URL("https://example.com/v1/messages"), + headers, + originalHeaders: new Headers(headers), + headerLog: JSON.stringify(Object.fromEntries(headers.entries())), + request: { + model: "deepseek-v4-pro", + log: "", + message: { + model: "deepseek-v4-pro", + thinking: { type: "disabled" }, + output_config: { effort: "max" }, + messages: [{ role: "user", content: [{ type: "text", text: "subagent task" }] }], + }, + }, + userAgent: null, + context: null, + clientAbortSignal: null, + userName: "test-user", + authState: { success: true, user: null, key: null, apiKey: null }, + provider: null, + messageContext: { id: 123, createdAt: new Date(), user: { id: 1 }, key: {}, apiKey: "k" }, + sessionId: null, + requestSequence: 1, + originalFormat: "claude", + providerType: null, + originalModelName: null, + originalUrlPathname: null, + providerChain: [], + cacheTtlResolved: null, + context1mApplied: false, + specialSettings: [], + cachedPriceData: undefined, + cachedBillingModelSource: undefined, + highConcurrencyModeEnabled: false, + isHeaderModified: () => false, + setHighConcurrencyModeEnabled(enabled: boolean) { + this.highConcurrencyModeEnabled = enabled; + }, + shouldPersistSessionDebugArtifacts() { + return !this.highConcurrencyModeEnabled; + }, + shouldTrackSessionObservability() { + return !this.highConcurrencyModeEnabled; + }, + }); + + return session as any; +} + +function createAnthropicProvider(): Provider { + return { + id: 1, + name: "deepseek-anthropic", + providerType: "claude", + url: "https://api.deepseek.com/anthropic/v1/messages", + key: "k", + preserveClientIp: false, + priority: 0, + } as unknown as Provider; +} + +function okResponse(): Response { + const body = JSON.stringify({ type: "message", content: [{ type: "text", text: "ok" }] }); + return new Response(body, { + status: 200, + headers: { "content-type": "application/json", "content-length": String(body.length) }, + }); +} + +describe("ProxyForwarder - thinking effort conflict rectifier", () => { + beforeEach(() => { + vi.clearAllMocks(); + mocks.getCachedSystemSettings.mockResolvedValue({ + enableThinkingEffortConflictRectifier: true, + enableHighConcurrencyMode: false, + }); + }); + + test("命中 DeepSeek thinking/reasoning_effort 冲突 400 时应剥离 effort 字段并对同供应商重试一次", async () => { + const session = createSession(); + session.setProvider(createAnthropicProvider()); + + const doForward = vi.spyOn(ProxyForwarder as any, "doForward"); + + doForward.mockImplementationOnce(async () => { + throw new ProxyError(DEEPSEEK_CONFLICT_ERROR, 400, { + body: "", + providerId: 1, + providerName: "deepseek-anthropic", + }); + }); + + doForward.mockImplementationOnce(async (s: ProxySession) => { + const msg = s.request.message as any; + expect("output_config" in msg).toBe(false); + expect("reasoning_effort" in msg).toBe(false); + expect(msg.thinking).toEqual({ type: "disabled" }); + return okResponse(); + }); + + const response = await ProxyForwarder.send(session); + + expect(response.status).toBe(200); + expect(doForward).toHaveBeenCalledTimes(2); + expect(session.getProviderChain()?.length).toBeGreaterThanOrEqual(2); + + const special = JSON.stringify(session.getSpecialSettings()); + expect(special).toContain("thinking_effort_conflict_rectifier"); + expect(special).not.toContain("thinking_signature_rectifier"); + expect(mocks.updateMessageRequestDetails).toHaveBeenCalledTimes(1); + }); + + test("开关关闭时不整流也不重试", async () => { + mocks.getCachedSystemSettings.mockResolvedValue({ + enableThinkingEffortConflictRectifier: false, + enableHighConcurrencyMode: false, + }); + + const session = createSession(); + session.setProvider(createAnthropicProvider()); + + const doForward = vi.spyOn(ProxyForwarder as any, "doForward"); + doForward.mockImplementation(async () => { + throw new ProxyError(DEEPSEEK_CONFLICT_ERROR, 400, { + body: "", + providerId: 1, + providerName: "deepseek-anthropic", + }); + }); + + await expect(ProxyForwarder.send(session)).rejects.toThrow(); + + // 开关关闭:请求体不被整流(常规重试策略仍可能多次尝试,但都带原始字段) + const message = session.request.message as Record; + expect(message.output_config).toEqual({ effort: "max" }); + expect(JSON.stringify(session.getSpecialSettings() ?? [])).not.toContain( + "thinking_effort_conflict_rectifier" + ); + expect(doForward.mock.calls.length).toBeGreaterThanOrEqual(1); + }); + + test("请求体不含冲突字段时记录 not_applicable 且不重试", async () => { + const session = createSession(); + const message = session.request.message as Record; + delete message.output_config; + (message.thinking as Record).type = "enabled"; + session.setProvider(createAnthropicProvider()); + + const doForward = vi.spyOn(ProxyForwarder as any, "doForward"); + doForward.mockImplementation(async () => { + throw new ProxyError(DEEPSEEK_CONFLICT_ERROR, 400, { + body: "", + providerId: 1, + providerName: "deepseek-anthropic", + }); + }); + + await expect(ProxyForwarder.send(session)).rejects.toThrow(); + expect(doForward).toHaveBeenCalledTimes(1); + + const special = JSON.stringify(session.getSpecialSettings()); + expect(special).toContain("thinking_effort_conflict_rectifier"); + expect(special).toContain('"hit":false'); + }); + + test("同一供应商最多整流重试一次(第二次命中不再重试)", async () => { + const session = createSession(); + session.setProvider(createAnthropicProvider()); + + const doForward = vi.spyOn(ProxyForwarder as any, "doForward"); + doForward.mockImplementation(async (s: ProxySession) => { + // 即使整流移除了 effort 字段,上游仍持续返回同样错误 + void s; + throw new ProxyError(DEEPSEEK_CONFLICT_ERROR, 400, { + body: "", + providerId: 1, + providerName: "deepseek-anthropic", + }); + }); + + await expect(ProxyForwarder.send(session)).rejects.toThrow(); + expect(doForward).toHaveBeenCalledTimes(2); + }); +}); diff --git a/tests/unit/repository/system-config-update-missing-columns.test.ts b/tests/unit/repository/system-config-update-missing-columns.test.ts index 20aaaa318..addafccb0 100644 --- a/tests/unit/repository/system-config-update-missing-columns.test.ts +++ b/tests/unit/repository/system-config-update-missing-columns.test.ts @@ -291,6 +291,63 @@ describe("SystemSettings:数据库缺列时的保存兜底", () => { vi.useRealTimers(); }); + test("getSystemSettings 在仅缺 enable_thinking_effort_conflict_rectifier 新列时应降级读取并默认开启", async () => { + vi.resetModules(); + + const now = new Date("2026-01-04T00:00:00.000Z"); + vi.useFakeTimers(); + vi.setSystemTime(now); + + // 第一次 select(fullSelection) 因新列缺失而抛 42703; + // 第二次 select(selectionWithoutEffortConflict) 命中——验证新列已加入降级链最外层。 + const selectMock = vi + .fn() + .mockReturnValueOnce(createRejectedThenableQuery({ code: "42703" })) + .mockReturnValueOnce( + createThenableQuery([ + { + id: 1, + siteTitle: "Claude Code Hub", + allowGlobalUsageView: false, + currencyDisplay: "USD", + billingModelSource: "original", + codexPriorityBillingSource: "requested", + enableHttp2: true, + enableThinkingSignatureRectifier: true, + enableThinkingBudgetRectifier: true, + createdAt: now, + updatedAt: now, + }, + ]) + ); + + vi.doMock("@/drizzle/db", () => ({ + db: { + select: selectMock, + update: vi.fn(() => createThenableQuery([])), + insert: vi.fn(() => createThenableQuery([])), + execute: vi.fn(async () => ({ count: 0 })), + }, + })); + + const { getSystemSettings } = await import("@/repository/system-config"); + + const result = await getSystemSettings(); + + // 降级读取成功(未抛错)。 + expect(selectMock).toHaveBeenCalledTimes(2); + expect(result.siteTitle).toBe("Claude Code Hub"); + expect(result.enableHttp2).toBe(true); + + // 关键回归保护:第二次 select 必须恰好剥离了新列(最外层降级), + // 而非旧行为先剥离 billHedgeLosers。若新列未加入降级链最外层,下面两条断言会失败。 + const secondSelection = selectMock.mock.calls[1]?.[0] as Record; + expect(secondSelection).not.toHaveProperty("enableThinkingEffortConflictRectifier"); + expect(secondSelection).toHaveProperty("billHedgeLosers"); + + vi.useRealTimers(); + }); + test("getSystemSettings 在缺少新列且无记录时应使用降级插入初始化", async () => { vi.resetModules();