From b61e1d7855fb9de2949f8ae16f98c31eb7db5e1a Mon Sep 17 00:00:00 2001 From: max Date: Thu, 11 Jun 2026 13:36:47 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix(proxy):=20=E5=B0=86=20messages=20?= =?UTF-8?q?=E4=B8=AD=E7=9A=84=20role:"system"=20=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E5=90=88=E5=B9=B6=E5=88=B0=E9=A1=B6=E5=B1=82=20system=20?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=EF=BC=8C=E4=BF=AE=E5=A4=8D=E4=B8=A5=E6=A0=BC?= =?UTF-8?q?=E4=B8=8A=E6=B8=B8=20400=20(#1265)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Claude Code 2.1.172+ 配合自定义模型名时会在 messages 数组中注入独立的 role:"system" 消息(skills 列表)。Anthropic 官方 API 接受该写法,但 Vertex/Bedrock 类严格校验的兼容上游会以 400 拒绝,导致 cchub 重试耗尽 全部供应商后熔断返回 503。 - 新增 system message rectifier(proactive,参照 billing-header-rectifier 模式):转发前将 role:"system" 消息从 messages 中取出,文本内容以 {type:"text",text} block 形式追加到顶层 system 字段末尾,原地变异并 记录 system_message_rectifier specialSetting 审计 - 在 forwarder 中对 claude/claude-auth 供应商启用,且置于 billing header rectifier 之前,确保 system 消息内携带的 billing header 块在搬移后仍 会被剥离 - 新增系统设置开关 enableSystemMessageRectifier(默认开启),覆盖 schema/迁移/缓存/类型/校验/Server Action/REST/OpenAPI/UI 全链路, 并按既有模式接入 system_settings 缺列降级链(读取与更新),保证 迁移前部署的实例行为不回退 - 设置页新增开关项,补全 zh-CN/zh-TW/en/ja/ru 五语言文案 - 新增 16 个单元测试覆盖 string/array content、顶层 system 各形态、 保序、原地变异及与 billing rectifier 的执行顺序组合 Fixes #1265 --- drizzle/0105_easy_cyclops.sql | 1 + drizzle/meta/0105_snapshot.json | 4535 +++++++++++++++++ drizzle/meta/_journal.json | 7 + messages/en/settings/config.json | 2 + messages/ja/settings/config.json | 2 + messages/ru/settings/config.json | 2 + messages/zh-CN/settings/config.json | 2 + messages/zh-TW/settings/config.json | 2 + src/actions/system-config.ts | 2 + .../_components/system-settings-form.tsx | 29 + src/app/[locale]/settings/config/page.tsx | 1 + src/app/api/admin/system-config/route.ts | 1 + src/app/v1/_lib/proxy/forwarder.ts | 30 + .../v1/_lib/proxy/system-message-rectifier.ts | 117 + src/drizzle/schema.ts | 7 + src/lib/api-client/v1/openapi-types.gen.ts | 6 + src/lib/api/v1/schemas/system-config.ts | 3 + src/lib/config/system-settings-cache.ts | 3 + src/lib/utils/special-settings.ts | 2 + src/lib/validation/schemas.ts | 2 + src/repository/_shared/transformers.ts | 1 + src/repository/system-config.ts | 93 +- src/types/special-settings.ts | 17 + src/types/system-config.ts | 8 + .../proxy/system-message-rectifier.test.ts | 262 + 25 files changed, 5121 insertions(+), 16 deletions(-) create mode 100644 drizzle/0105_easy_cyclops.sql create mode 100644 drizzle/meta/0105_snapshot.json create mode 100644 src/app/v1/_lib/proxy/system-message-rectifier.ts create mode 100644 tests/unit/proxy/system-message-rectifier.test.ts diff --git a/drizzle/0105_easy_cyclops.sql b/drizzle/0105_easy_cyclops.sql new file mode 100644 index 000000000..8686d7327 --- /dev/null +++ b/drizzle/0105_easy_cyclops.sql @@ -0,0 +1 @@ +ALTER TABLE "system_settings" ADD COLUMN "enable_system_message_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..d197c05d7 --- /dev/null +++ b/drizzle/meta/0105_snapshot.json @@ -0,0 +1,4535 @@ +{ + "id": "41e59ca6-df3d-4872-8057-4f602f188735", + "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_billing_header_rectifier": { + "name": "enable_billing_header_rectifier", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "enable_system_message_rectifier": { + "name": "enable_system_message_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..7b08c5eed 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": 1781154049993, + "tag": "0105_easy_cyclops", + "breakpoints": true } ] } \ No newline at end of file diff --git a/messages/en/settings/config.json b/messages/en/settings/config.json index 6d1447d2a..0f1b17dc2 100644 --- a/messages/en/settings/config.json +++ b/messages/en/settings/config.json @@ -68,6 +68,8 @@ "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).", "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).", + "enableSystemMessageRectifier": "Enable System Message Rectifier", + "enableSystemMessageRectifierDesc": "Merges role:\"system\" entries in the messages array (injected by Claude Code 2.1.172+ when using custom model names) into the top-level system field, preventing strict Anthropic-compatible upstreams such as Vertex/Bedrock from returning 400 errors (enabled by default).", "enableResponseInputRectifier": "Enable Response Input Rectifier", "enableResponseInputRectifierDesc": "Automatically normalizes non-array input (string shortcut or single message object) in /v1/responses requests to standard array format before processing (enabled by default).", "allowNonConversationEndpointProviderFallback": "Allow cross-provider fallback for non-conversation endpoints", diff --git a/messages/ja/settings/config.json b/messages/ja/settings/config.json index 68a55077c..43cd97759 100644 --- a/messages/ja/settings/config.json +++ b/messages/ja/settings/config.json @@ -68,6 +68,8 @@ "enableThinkingBudgetRectifierDesc": "Anthropic プロバイダーで budget_tokens < 1024 エラーが発生した場合、thinking 予算を最大値(32000)に設定し、必要に応じて max_tokens を 64000 に設定して1回だけ再試行します(既定で有効)。", "enableBillingHeaderRectifier": "課金ヘッダー整流を有効化", "enableBillingHeaderRectifierDesc": "Claude Code クライアントが system プロンプトに注入する x-anthropic-billing-header テキストブロックを事前に削除し、Amazon Bedrock などの非ネイティブ Anthropic 上流による 400 エラーを防止します(既定で有効)。", + "enableSystemMessageRectifier": "システムメッセージ整流を有効化", + "enableSystemMessageRectifierDesc": "messages 配列内の role:\"system\" メッセージ(カスタムモデル名使用時に Claude Code 2.1.172+ が注入)をトップレベルの system フィールドに統合し、Vertex/Bedrock などの厳格な Anthropic 互換上流による 400 エラーを防止します(既定で有効)。", "enableResponseInputRectifier": "Response Input 整流器を有効化", "enableResponseInputRectifierDesc": "/v1/responses リクエストの非配列 input(文字列ショートカットまたは role/type 付き単一メッセージオブジェクト)を処理前に標準の配列形式に自動正規化します(既定で有効)。", "allowNonConversationEndpointProviderFallback": "非会話エンドポイントのクロスプロバイダ fallback を許可", diff --git a/messages/ru/settings/config.json b/messages/ru/settings/config.json index 52090a13a..5a205b02f 100644 --- a/messages/ru/settings/config.json +++ b/messages/ru/settings/config.json @@ -68,6 +68,8 @@ "enableThinkingBudgetRectifierDesc": "Если Anthropic-провайдер возвращает ошибку budget_tokens < 1024, автоматически устанавливает thinking budget на максимум (32000) и при необходимости max_tokens на 64000, затем повторяет запрос один раз (включено по умолчанию).", "enableBillingHeaderRectifier": "Включить исправление billing-заголовка", "enableBillingHeaderRectifierDesc": "Проактивно удаляет текстовые блоки x-anthropic-billing-header, добавленные клиентом Claude Code в системный промпт, предотвращая ошибки 400 от Amazon Bedrock и других не-Anthropic провайдеров (включено по умолчанию).", + "enableSystemMessageRectifier": "Включить исправление system-сообщений", + "enableSystemMessageRectifierDesc": "Переносит сообщения role:\"system\" из массива messages (добавляются Claude Code 2.1.172+ при использовании пользовательских имён моделей) в поле system верхнего уровня, предотвращая ошибки 400 от строгих Anthropic-совместимых провайдеров, таких как Vertex/Bedrock (включено по умолчанию).", "enableResponseInputRectifier": "Включить исправление Response Input", "enableResponseInputRectifierDesc": "Автоматически нормализует не-массивные input (строковые сокращения или одиночные объекты сообщений с role/type) в запросах /v1/responses в стандартный формат массива перед обработкой (включено по умолчанию).", "allowNonConversationEndpointProviderFallback": "Разрешить cross-provider fallback для не-диалоговых endpoint", diff --git a/messages/zh-CN/settings/config.json b/messages/zh-CN/settings/config.json index ecd85ddbd..7601a3f2a 100644 --- a/messages/zh-CN/settings/config.json +++ b/messages/zh-CN/settings/config.json @@ -66,6 +66,8 @@ "enableThinkingBudgetRectifierDesc": "当 Anthropic 类型供应商返回 budget_tokens < 1024 错误时,自动将 thinking 预算设为最大值(32000),并在需要时将 max_tokens 设为 64000,然后重试一次(默认开启)。", "enableBillingHeaderRectifier": "启用计费标头整流器", "enableBillingHeaderRectifierDesc": "主动移除 Claude Code 客户端注入到 system 提示中的 x-anthropic-billing-header 文本块,防止 Amazon Bedrock 等非原生 Anthropic 上游返回 400 错误(默认开启)。", + "enableSystemMessageRectifier": "启用系统消息整流器", + "enableSystemMessageRectifierDesc": "将 messages 数组中的 role:\"system\" 消息(Claude Code 2.1.172+ 配合自定义模型名时注入)合并到顶层 system 字段,防止 Vertex/Bedrock 等严格校验的 Anthropic 兼容上游返回 400 错误(默认开启)。", "enableResponseInputRectifier": "启用 Response Input 整流器", "enableResponseInputRectifierDesc": "自动将 /v1/responses 请求中的非数组 input(字符串简写或带 role/type 的单消息对象)规范化为标准数组格式后再处理(默认开启)。", "allowNonConversationEndpointProviderFallback": "允许非对话端点跨供应商 fallback", diff --git a/messages/zh-TW/settings/config.json b/messages/zh-TW/settings/config.json index 7c94a809d..a06120faa 100644 --- a/messages/zh-TW/settings/config.json +++ b/messages/zh-TW/settings/config.json @@ -68,6 +68,8 @@ "enableThinkingBudgetRectifierDesc": "當 Anthropic 類型供應商返回 budget_tokens < 1024 錯誤時,自動將 thinking 預算設為最大值(32000),並在需要時將 max_tokens 設為 64000,然後重試一次(預設開啟)。", "enableBillingHeaderRectifier": "啟用計費標頭整流器", "enableBillingHeaderRectifierDesc": "主動移除 Claude Code 客戶端注入到 system 提示中的 x-anthropic-billing-header 文字區塊,防止 Amazon Bedrock 等非原生 Anthropic 上游回傳 400 錯誤(預設開啟)。", + "enableSystemMessageRectifier": "啟用系統訊息整流器", + "enableSystemMessageRectifierDesc": "將 messages 陣列中的 role:\"system\" 訊息(Claude Code 2.1.172+ 配合自訂模型名稱時注入)合併到頂層 system 欄位,防止 Vertex/Bedrock 等嚴格校驗的 Anthropic 相容上游回傳 400 錯誤(預設開啟)。", "enableResponseInputRectifier": "啟用 Response Input 整流器", "enableResponseInputRectifierDesc": "自動將 /v1/responses 請求中的非陣列 input(字串簡寫或帶 role/type 的單訊息物件)規範化為標準陣列格式後再處理(預設開啟)。", "allowNonConversationEndpointProviderFallback": "允許非對話端點跨供應商 fallback", diff --git a/src/actions/system-config.ts b/src/actions/system-config.ts index eb4a24636..50fe334c0 100644 --- a/src/actions/system-config.ts +++ b/src/actions/system-config.ts @@ -74,6 +74,7 @@ export async function saveSystemSettings(formData: { enableThinkingSignatureRectifier?: boolean; enableThinkingBudgetRectifier?: boolean; enableBillingHeaderRectifier?: boolean; + enableSystemMessageRectifier?: boolean; enableResponseInputRectifier?: boolean; allowNonConversationEndpointProviderFallback?: boolean; fakeStreamingWhitelist?: FakeStreamingWhitelistEntry[]; @@ -126,6 +127,7 @@ export async function saveSystemSettings(formData: { enableThinkingSignatureRectifier: validated.enableThinkingSignatureRectifier, enableThinkingBudgetRectifier: validated.enableThinkingBudgetRectifier, enableBillingHeaderRectifier: validated.enableBillingHeaderRectifier, + enableSystemMessageRectifier: validated.enableSystemMessageRectifier, enableResponseInputRectifier: validated.enableResponseInputRectifier, allowNonConversationEndpointProviderFallback: validated.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..fe3ae75f8 100644 --- a/src/app/[locale]/settings/config/_components/system-settings-form.tsx +++ b/src/app/[locale]/settings/config/_components/system-settings-form.tsx @@ -74,6 +74,7 @@ interface SystemSettingsFormProps { | "interceptAnthropicWarmupRequests" | "enableThinkingSignatureRectifier" | "enableBillingHeaderRectifier" + | "enableSystemMessageRectifier" | "enableResponseInputRectifier" | "enableThinkingBudgetRectifier" | "allowNonConversationEndpointProviderFallback" @@ -151,6 +152,9 @@ export function SystemSettingsForm({ initialSettings }: SystemSettingsFormProps) const [enableBillingHeaderRectifier, setEnableBillingHeaderRectifier] = useState( initialSettings.enableBillingHeaderRectifier ); + const [enableSystemMessageRectifier, setEnableSystemMessageRectifier] = useState( + initialSettings.enableSystemMessageRectifier + ); const [enableResponseInputRectifier, setEnableResponseInputRectifier] = useState( initialSettings.enableResponseInputRectifier ); @@ -313,6 +317,7 @@ export function SystemSettingsForm({ initialSettings }: SystemSettingsFormProps) interceptAnthropicWarmupRequests, enableThinkingSignatureRectifier, enableBillingHeaderRectifier, + enableSystemMessageRectifier, enableResponseInputRectifier, allowNonConversationEndpointProviderFallback, fakeStreamingWhitelist: sanitizedFakeStreamingWhitelist, @@ -353,6 +358,7 @@ export function SystemSettingsForm({ initialSettings }: SystemSettingsFormProps) setInterceptAnthropicWarmupRequests(result.data.interceptAnthropicWarmupRequests); setEnableThinkingSignatureRectifier(result.data.enableThinkingSignatureRectifier); setEnableBillingHeaderRectifier(result.data.enableBillingHeaderRectifier); + setEnableSystemMessageRectifier(result.data.enableSystemMessageRectifier); setEnableResponseInputRectifier(result.data.enableResponseInputRectifier); setAllowNonConversationEndpointProviderFallback( result.data.allowNonConversationEndpointProviderFallback @@ -819,6 +825,29 @@ export function SystemSettingsForm({ initialSettings }: SystemSettingsFormProps) /> + {/* Enable System Message Rectifier */} +
+
+
+ +
+
+

+ {t("enableSystemMessageRectifier")} +

+

+ {t("enableSystemMessageRectifierDesc")} +

+
+
+ setEnableSystemMessageRectifier(checked)} + disabled={isPending} + /> +
+ {/* Enable Response Input Rectifier */}
diff --git a/src/app/[locale]/settings/config/page.tsx b/src/app/[locale]/settings/config/page.tsx index 46b992bd0..019263832 100644 --- a/src/app/[locale]/settings/config/page.tsx +++ b/src/app/[locale]/settings/config/page.tsx @@ -62,6 +62,7 @@ async function SettingsConfigContent({ locale }: { locale: string }) { enableThinkingSignatureRectifier: settings.enableThinkingSignatureRectifier, enableThinkingBudgetRectifier: settings.enableThinkingBudgetRectifier, enableBillingHeaderRectifier: settings.enableBillingHeaderRectifier, + enableSystemMessageRectifier: settings.enableSystemMessageRectifier, enableResponseInputRectifier: settings.enableResponseInputRectifier, allowNonConversationEndpointProviderFallback: settings.allowNonConversationEndpointProviderFallback, diff --git a/src/app/api/admin/system-config/route.ts b/src/app/api/admin/system-config/route.ts index 93cc54a68..9611b76eb 100644 --- a/src/app/api/admin/system-config/route.ts +++ b/src/app/api/admin/system-config/route.ts @@ -77,6 +77,7 @@ export async function POST(req: Request) { enableThinkingSignatureRectifier: validated.enableThinkingSignatureRectifier, enableThinkingBudgetRectifier: validated.enableThinkingBudgetRectifier, enableBillingHeaderRectifier: validated.enableBillingHeaderRectifier, + enableSystemMessageRectifier: validated.enableSystemMessageRectifier, enableResponseInputRectifier: validated.enableResponseInputRectifier, enableCodexSessionIdCompletion: validated.enableCodexSessionIdCompletion, enableClaudeMetadataUserIdInjection: validated.enableClaudeMetadataUserIdInjection, diff --git a/src/app/v1/_lib/proxy/forwarder.ts b/src/app/v1/_lib/proxy/forwarder.ts index a568f0717..0bbaded55 100644 --- a/src/app/v1/_lib/proxy/forwarder.ts +++ b/src/app/v1/_lib/proxy/forwarder.ts @@ -92,6 +92,7 @@ import { ProxyProviderResolver } from "./provider-selector"; import { finalizeHedgeLoserBilling } from "./response-handler"; import type { ProxySession } from "./session"; import { setDeferredStreamingFinalization } from "./stream-finalization"; +import { rectifySystemMessages } from "./system-message-rectifier"; import { detectThinkingBudgetRectifierTrigger, rectifyThinkingBudget, @@ -2447,6 +2448,35 @@ export class ProxyForwarder { // Anthropic 供应商级参数覆写(默认 inherit=遵循客户端) // 说明:允许管理员在供应商层面强制覆写 max_tokens 和 thinking.budget_tokens if (provider.providerType === "claude" || provider.providerType === "claude-auth") { + // System message rectifier: move role:"system" entries from messages array + // into the top-level system field (Claude Code 2.1.172+ with custom model names). + // Must run before the billing header rectifier so any billing-header text block + // carried inside a role:"system" message lands in `system` before stripping. + { + const settings = await getCachedSystemSettings(); + const systemMessageRectifierEnabled = settings.enableSystemMessageRectifier ?? true; + if (systemMessageRectifierEnabled) { + const systemMessageResult = rectifySystemMessages( + session.request.message as Record + ); + if (systemMessageResult.applied) { + session.addSpecialSetting({ + type: "system_message_rectifier", + scope: "request", + hit: true, + movedCount: systemMessageResult.movedCount, + extractedValues: systemMessageResult.extractedValues, + }); + logger.info("ProxyForwarder: System message rectifier applied", { + providerId: provider.id, + providerName: provider.name, + movedCount: systemMessageResult.movedCount, + }); + await persistSpecialSettings(session); + } + } + } + // Billing header rectifier: proactively strip x-anthropic-billing-header from system prompt { const settings = await getCachedSystemSettings(); diff --git a/src/app/v1/_lib/proxy/system-message-rectifier.ts b/src/app/v1/_lib/proxy/system-message-rectifier.ts new file mode 100644 index 000000000..05168f696 --- /dev/null +++ b/src/app/v1/_lib/proxy/system-message-rectifier.ts @@ -0,0 +1,117 @@ +/** + * System Message Rectifier - Proactive (pre-send) rectifier for Claude Code client + * `role: "system"` message injection. + * + * Problem: Claude Code client v2.1.172+ (behavior introduced after v2.1.153) injects + * the skills list as a standalone `{"role": "system", "content": "..."}` entry in the + * `messages` array when the configured model name is not a built-in official Claude + * model ID. The official Anthropic API accepts this, but strict Anthropic-compatible + * upstreams (Vertex / Bedrock style conversion layers) only allow `user` / `assistant` + * roles in `messages` and reject the request with a 400 error. + * + * Solution: Before forwarding, extract `role: "system"` entries from `messages` and + * merge their text content into the top-level `system` field (appending as + * `{type: "text", text}` blocks). This matches how the official API interprets such + * messages, so permissive upstreams see no behavior change while strict upstreams no + * longer reject the request. + */ + +export type SystemMessageRectifierResult = { + applied: boolean; + movedCount: number; + extractedValues: string[]; +}; + +const NOOP_RESULT: SystemMessageRectifierResult = { + applied: false, + movedCount: 0, + extractedValues: [], +}; + +type TextBlock = { type: "text"; text: string; [key: string]: unknown }; + +function isTextBlock(block: unknown): block is TextBlock { + return ( + typeof block === "object" && + block !== null && + (block as Record).type === "text" && + typeof (block as Record).text === "string" + ); +} + +/** + * Convert the content of a `role: "system"` message into text blocks suitable for + * the top-level `system` array. String content becomes a single text block; array + * content keeps its text blocks as-is (preserving extra fields like cache_control) + * and drops non-text blocks, which are not valid in a system prompt anyway. + */ +function contentToTextBlocks(content: unknown): TextBlock[] { + if (typeof content === "string") { + return content.length > 0 ? [{ type: "text", text: content }] : []; + } + if (Array.isArray(content)) { + return content.filter(isTextBlock); + } + return []; +} + +/** + * Move `role: "system"` entries out of the `messages` array and append their text + * content to the top-level `system` field. Mutates the message object in place + * (matches existing rectifier conventions). + */ +export function rectifySystemMessages( + message: Record +): SystemMessageRectifierResult { + const messages = message.messages; + if (!Array.isArray(messages)) { + return { ...NOOP_RESULT }; + } + + const extractedBlocks: TextBlock[] = []; + const extractedValues: string[] = []; + const remaining: unknown[] = []; + let movedCount = 0; + + for (const entry of messages) { + if ( + entry && + typeof entry === "object" && + (entry as Record).role === "system" + ) { + movedCount++; + const blocks = contentToTextBlocks((entry as Record).content); + extractedBlocks.push(...blocks); + extractedValues.push(...blocks.map((block) => block.text)); + } else { + remaining.push(entry); + } + } + + if (movedCount === 0) { + return { ...NOOP_RESULT }; + } + + // Mutate in place: replace messages array contents + messages.length = 0; + for (const entry of remaining) { + messages.push(entry); + } + + if (extractedBlocks.length > 0) { + const system = message.system; + if (system === undefined || system === null) { + message.system = extractedBlocks; + } else if (typeof system === "string") { + const head: TextBlock[] = system.length > 0 ? [{ type: "text", text: system }] : []; + message.system = [...head, ...extractedBlocks]; + } else if (Array.isArray(system)) { + system.push(...extractedBlocks); + } else { + // Unknown system type: replace with extracted blocks to avoid dropping content + message.system = extractedBlocks; + } + } + + return { applied: true, movedCount, extractedValues }; +} diff --git a/src/drizzle/schema.ts b/src/drizzle/schema.ts index 034c07a1f..b933a127e 100644 --- a/src/drizzle/schema.ts +++ b/src/drizzle/schema.ts @@ -810,6 +810,13 @@ export const systemSettings = pgTable('system_settings', { .notNull() .default(true), + // system message 整流器(默认开启) + // 开启后:将 messages 数组中的 role:"system" 消息合并到顶层 system 字段, + // 避免严格校验的 Anthropic 兼容上游(Vertex/Bedrock 类)返回 400 + enableSystemMessageRectifier: boolean('enable_system_message_rectifier') + .notNull() + .default(true), + // Response API input 整流器(默认开启) // 开启后:当 /v1/responses 端点收到非数组 input 时,自动规范化为数组格式 enableResponseInputRectifier: boolean('enable_response_input_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..e501cd37a 100644 --- a/src/lib/api-client/v1/openapi-types.gen.ts +++ b/src/lib/api-client/v1/openapi-types.gen.ts @@ -11657,6 +11657,8 @@ export interface operations { enableThinkingBudgetRectifier: boolean; /** @description Whether billing-header rectifier is enabled. */ enableBillingHeaderRectifier: boolean; + /** @description Whether system message rectifier is enabled. */ + enableSystemMessageRectifier: boolean; /** @description Whether Responses API input rectifier is enabled. */ enableResponseInputRectifier: boolean; /** @description Whether non-conversation endpoints may fall back across providers. */ @@ -11915,6 +11917,8 @@ export interface operations { enableThinkingBudgetRectifier?: boolean; /** @description Whether billing-header rectifier is enabled. */ enableBillingHeaderRectifier?: boolean; + /** @description Whether system message rectifier is enabled. */ + enableSystemMessageRectifier?: boolean; /** @description Whether Responses API input rectifier is enabled. */ enableResponseInputRectifier?: boolean; /** @description Whether non-conversation endpoints may fall back across providers. */ @@ -12046,6 +12050,8 @@ export interface operations { enableThinkingBudgetRectifier: boolean; /** @description Whether billing-header rectifier is enabled. */ enableBillingHeaderRectifier: boolean; + /** @description Whether system message rectifier is enabled. */ + enableSystemMessageRectifier: boolean; /** @description Whether Responses API input rectifier is enabled. */ enableResponseInputRectifier: boolean; /** @description Whether non-conversation endpoints may fall back across providers. */ diff --git a/src/lib/api/v1/schemas/system-config.ts b/src/lib/api/v1/schemas/system-config.ts index 92dc06ecb..b75840d4e 100644 --- a/src/lib/api/v1/schemas/system-config.ts +++ b/src/lib/api/v1/schemas/system-config.ts @@ -129,6 +129,9 @@ export const SystemSettingsSchema = z enableBillingHeaderRectifier: z .boolean() .describe("Whether billing-header rectifier is enabled."), + enableSystemMessageRectifier: z + .boolean() + .describe("Whether system message rectifier is enabled."), enableResponseInputRectifier: z .boolean() .describe("Whether Responses API input rectifier is enabled."), diff --git a/src/lib/config/system-settings-cache.ts b/src/lib/config/system-settings-cache.ts index 232349d56..3ef550b63 100644 --- a/src/lib/config/system-settings-cache.ts +++ b/src/lib/config/system-settings-cache.ts @@ -42,6 +42,7 @@ const DEFAULT_SETTINGS: Pick< | "enableThinkingSignatureRectifier" | "enableThinkingBudgetRectifier" | "enableBillingHeaderRectifier" + | "enableSystemMessageRectifier" | "enableResponseInputRectifier" | "allowNonConversationEndpointProviderFallback" | "fakeStreamingWhitelist" @@ -61,6 +62,7 @@ const DEFAULT_SETTINGS: Pick< enableThinkingSignatureRectifier: true, enableThinkingBudgetRectifier: true, enableBillingHeaderRectifier: true, + enableSystemMessageRectifier: true, enableResponseInputRectifier: true, // 安全敏感开关:冷缓存 / DB 读取失败时 fail-closed,避免意外重新开启跨供应商 raw fallback。 allowNonConversationEndpointProviderFallback: false, @@ -149,6 +151,7 @@ export async function getCachedSystemSettings(): Promise { enableThinkingSignatureRectifier: DEFAULT_SETTINGS.enableThinkingSignatureRectifier, enableThinkingBudgetRectifier: DEFAULT_SETTINGS.enableThinkingBudgetRectifier, enableBillingHeaderRectifier: DEFAULT_SETTINGS.enableBillingHeaderRectifier, + enableSystemMessageRectifier: DEFAULT_SETTINGS.enableSystemMessageRectifier, enableResponseInputRectifier: DEFAULT_SETTINGS.enableResponseInputRectifier, allowNonConversationEndpointProviderFallback: DEFAULT_SETTINGS.allowNonConversationEndpointProviderFallback, diff --git a/src/lib/utils/special-settings.ts b/src/lib/utils/special-settings.ts index a5f31c028..568f8dbaf 100644 --- a/src/lib/utils/special-settings.ts +++ b/src/lib/utils/special-settings.ts @@ -107,6 +107,8 @@ function buildSettingKey(setting: SpecialSetting): string { ]); case "billing_header_rectifier": return JSON.stringify([setting.type, setting.hit, setting.removedCount]); + case "system_message_rectifier": + return JSON.stringify([setting.type, setting.hit, setting.movedCount]); case "gemini_google_search_override": return JSON.stringify([ setting.type, diff --git a/src/lib/validation/schemas.ts b/src/lib/validation/schemas.ts index 368f564ab..58c4ae7bc 100644 --- a/src/lib/validation/schemas.ts +++ b/src/lib/validation/schemas.ts @@ -1007,6 +1007,8 @@ export const UpdateSystemSettingsSchema = z.object({ enableThinkingBudgetRectifier: z.boolean().optional(), // billing header 整流器(可选) enableBillingHeaderRectifier: z.boolean().optional(), + // system message 整流器(可选) + enableSystemMessageRectifier: z.boolean().optional(), // Response API input 整流器(可选) enableResponseInputRectifier: z.boolean().optional(), // 非对话端点跨供应商 fallback(可选) diff --git a/src/repository/_shared/transformers.ts b/src/repository/_shared/transformers.ts index e26969ce0..392edd36b 100644 --- a/src/repository/_shared/transformers.ts +++ b/src/repository/_shared/transformers.ts @@ -267,6 +267,7 @@ export function toSystemSettings(dbSettings: any): SystemSettings { enableThinkingSignatureRectifier: dbSettings?.enableThinkingSignatureRectifier ?? true, enableThinkingBudgetRectifier: dbSettings?.enableThinkingBudgetRectifier ?? true, enableBillingHeaderRectifier: dbSettings?.enableBillingHeaderRectifier ?? true, + enableSystemMessageRectifier: dbSettings?.enableSystemMessageRectifier ?? true, enableResponseInputRectifier: dbSettings?.enableResponseInputRectifier ?? true, allowNonConversationEndpointProviderFallback: dbSettings?.allowNonConversationEndpointProviderFallback ?? true, diff --git a/src/repository/system-config.ts b/src/repository/system-config.ts index ab83f114d..bf26264c8 100644 --- a/src/repository/system-config.ts +++ b/src/repository/system-config.ts @@ -164,6 +164,7 @@ function createFallbackSettings(): SystemSettings { enableThinkingSignatureRectifier: true, enableThinkingBudgetRectifier: true, enableBillingHeaderRectifier: true, + enableSystemMessageRectifier: true, enableResponseInputRectifier: true, allowNonConversationEndpointProviderFallback: true, fakeStreamingWhitelist: DEFAULT_FAKE_STREAMING_WHITELIST.map((entry) => ({ @@ -280,6 +281,7 @@ export async function getSystemSettings(): Promise { updatedAt: systemSettings.updatedAt, }; const fullSelection = { + enableSystemMessageRectifier: systemSettings.enableSystemMessageRectifier, billHedgeLosers: systemSettings.billHedgeLosers, billNonSuccessfulRequests: systemSettings.billNonSuccessfulRequests, passThroughUpstreamErrorMessage: systemSettings.passThroughUpstreamErrorMessage, @@ -302,9 +304,33 @@ export async function getSystemSettings(): Promise { error, }); - // 最新降级:移除最近新增的 billHedgeLosers 列。 + // 最新降级:移除最近新增的 enableSystemMessageRectifier 列。 + const { + enableSystemMessageRectifier: _omitSystemMessageRectifier, + ...selectionWithoutSystemMessageRectifier + } = fullSelection; + + try { + const [row] = await db + .select(selectionWithoutSystemMessageRectifier) + .from(systemSettings) + .orderBy(asc(systemSettings.id)) + .limit(1); + return row ?? null; + } catch (systemMessageRectifierFallbackError) { + if (!isUndefinedColumnError(systemMessageRectifierFallbackError)) { + throw systemMessageRectifierFallbackError; + } + + logger.warn( + "system_settings 表除 enableSystemMessageRectifier 外仍有列缺失,继续回退到上一代字段集。", + { error: systemMessageRectifierFallbackError } + ); + } + + // 次新降级:移除 billHedgeLosers 列。 const { billHedgeLosers: _omitBillHedgeLosers, ...selectionWithoutBillHedgeLosers } = - fullSelection; + selectionWithoutSystemMessageRectifier; try { const [row] = await db @@ -640,6 +666,7 @@ export async function updateSystemSettings( updatedAt: systemSettings.updatedAt, }; const fullReturning = { + enableSystemMessageRectifier: systemSettings.enableSystemMessageRectifier, billHedgeLosers: systemSettings.billHedgeLosers, billNonSuccessfulRequests: systemSettings.billNonSuccessfulRequests, passThroughUpstreamErrorMessage: systemSettings.passThroughUpstreamErrorMessage, @@ -756,6 +783,11 @@ export async function updateSystemSettings( updates.enableBillingHeaderRectifier = payload.enableBillingHeaderRectifier; } + // system message 整流器开关(如果提供) + if (payload.enableSystemMessageRectifier !== undefined) { + updates.enableSystemMessageRectifier = payload.enableSystemMessageRectifier; + } + // Response API input 整流器开关(如果提供) if (payload.enableResponseInputRectifier !== undefined) { updates.enableResponseInputRectifier = payload.enableResponseInputRectifier; @@ -846,29 +878,58 @@ export async function updateSystemSettings( error, }); - // 最新降级:移除最近新增的 billHedgeLosers 列。 - const { billHedgeLosers: _omitUpdateBillHedgeLosers, ...updatesWithoutBillHedgeLosers } = - updates; - const { billHedgeLosers: _omitReturningBillHedgeLosers, ...returningWithoutBillHedgeLosers } = - fullReturning; + // 最新降级:移除最近新增的 enableSystemMessageRectifier 列。 + const { + enableSystemMessageRectifier: _omitUpdateSystemMessageRectifier, + ...updatesWithoutSystemMessageRectifier + } = updates; + const { + enableSystemMessageRectifier: _omitReturningSystemMessageRectifier, + ...returningWithoutSystemMessageRectifier + } = fullReturning; try { [updated] = await executor .update(systemSettings) - .set(updatesWithoutBillHedgeLosers) + .set(updatesWithoutSystemMessageRectifier) .where(eq(systemSettings.id, current.id)) - .returning(returningWithoutBillHedgeLosers); - } catch (billHedgeLosersFallbackError) { - if (!isUndefinedColumnError(billHedgeLosersFallbackError)) { - throw billHedgeLosersFallbackError; + .returning(returningWithoutSystemMessageRectifier); + } catch (systemMessageRectifierFallbackError) { + if (!isUndefinedColumnError(systemMessageRectifierFallbackError)) { + throw systemMessageRectifierFallbackError; } - logger.warn("system_settings 表除 billHedgeLosers 外仍有列缺失,继续降级更新。", { - error: billHedgeLosersFallbackError, - }); + logger.warn( + "system_settings 表除 enableSystemMessageRectifier 外仍有列缺失,继续降级更新。", + { error: systemMessageRectifierFallbackError } + ); + } + + // 次新降级:移除 billHedgeLosers 列。 + const { billHedgeLosers: _omitUpdateBillHedgeLosers, ...updatesWithoutBillHedgeLosers } = + updatesWithoutSystemMessageRectifier; + const { billHedgeLosers: _omitReturningBillHedgeLosers, ...returningWithoutBillHedgeLosers } = + returningWithoutSystemMessageRectifier; + + 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 列。 + // 次次新降级:移除 billNonSuccessfulRequests 列。 const { billNonSuccessfulRequests: _omitUpdateBillNonSuccessful, ...updatesWithoutBillNonSuccessful diff --git a/src/types/special-settings.ts b/src/types/special-settings.ts index a93b8d1fe..a1871104f 100644 --- a/src/types/special-settings.ts +++ b/src/types/special-settings.ts @@ -12,6 +12,7 @@ export type SpecialSetting = | ThinkingSignatureRectifierSpecialSetting | ThinkingBudgetRectifierSpecialSetting | BillingHeaderRectifierSpecialSetting + | SystemMessageRectifierSpecialSetting | CodexSessionIdCompletionSpecialSetting | ClaudeMetadataUserIdInjectionSpecialSetting | AnthropicEffortSpecialSetting @@ -193,6 +194,22 @@ export type BillingHeaderRectifierSpecialSetting = { extractedValues: string[]; }; +/** + * System message 整流器审计 + * + * 用于记录:当请求 messages 数组中包含 role:"system" 消息时 + * (Claude Code 2.1.172+ 配合自定义模型名会注入), + * 系统将其内容合并到顶层 system 字段的行为, + * 避免严格校验的 Anthropic 兼容上游(Vertex/Bedrock 类)返回 400。 + */ +export type SystemMessageRectifierSpecialSetting = { + type: "system_message_rectifier"; + scope: "request"; + hit: boolean; + movedCount: number; + extractedValues: string[]; +}; + export type ThinkingBudgetRectifierSpecialSetting = { type: "thinking_budget_rectifier"; scope: "request"; diff --git a/src/types/system-config.ts b/src/types/system-config.ts index e0080a37f..8e835bfa3 100644 --- a/src/types/system-config.ts +++ b/src/types/system-config.ts @@ -96,6 +96,11 @@ export interface SystemSettings { // 防止 Amazon Bedrock 等非原生 Anthropic 上游返回 400 错误 enableBillingHeaderRectifier: boolean; + // system message 整流器(默认开启) + // 目标:将 messages 数组中的 role:"system" 消息(Claude Code 2.1.172+ 配合自定义模型名注入) + // 合并到顶层 system 字段,避免严格校验的 Anthropic 兼容上游(Vertex/Bedrock 类)返回 400 错误 + enableSystemMessageRectifier: boolean; + // Response API input 整流器(默认开启) // 目标:当 /v1/responses 端点收到非数组 input(字符串或单对象)时, // 自动规范化为数组格式,确保下游处理兼容 OpenAI 完整规范 @@ -199,6 +204,9 @@ export interface UpdateSystemSettingsInput { // billing header 整流器(可选) enableBillingHeaderRectifier?: boolean; + // system message 整流器(可选) + enableSystemMessageRectifier?: boolean; + // Response API input 整流器(可选) enableResponseInputRectifier?: boolean; diff --git a/tests/unit/proxy/system-message-rectifier.test.ts b/tests/unit/proxy/system-message-rectifier.test.ts new file mode 100644 index 000000000..baa8ee521 --- /dev/null +++ b/tests/unit/proxy/system-message-rectifier.test.ts @@ -0,0 +1,262 @@ +import { describe, expect, test } from "vitest"; +import { rectifyBillingHeader } from "@/app/v1/_lib/proxy/billing-header-rectifier"; +import { rectifySystemMessages } from "@/app/v1/_lib/proxy/system-message-rectifier"; + +describe("rectifySystemMessages", () => { + test("system message with string content, no top-level system - moves it into system array", () => { + const message: Record = { + messages: [ + { role: "user", content: "hi" }, + { role: "system", content: "The following skills are available: ..." }, + ], + }; + + const result = rectifySystemMessages(message); + + expect(result.applied).toBe(true); + expect(result.movedCount).toBe(1); + expect(result.extractedValues).toEqual(["The following skills are available: ..."]); + expect(message.messages).toEqual([{ role: "user", content: "hi" }]); + expect(message.system).toEqual([ + { type: "text", text: "The following skills are available: ..." }, + ]); + }); + + test("system message with existing top-level system array - appends to the end", () => { + const message: Record = { + system: [{ type: "text", text: "You are a helpful assistant." }], + messages: [ + { role: "user", content: "hi" }, + { role: "system", content: "skills list" }, + ], + }; + + const result = rectifySystemMessages(message); + + expect(result.applied).toBe(true); + expect(message.system).toEqual([ + { type: "text", text: "You are a helpful assistant." }, + { type: "text", text: "skills list" }, + ]); + }); + + test("system message with existing top-level system string - converts string to leading text block", () => { + const message: Record = { + system: "You are a helpful assistant.", + messages: [ + { role: "user", content: "hi" }, + { role: "system", content: "skills list" }, + ], + }; + + const result = rectifySystemMessages(message); + + expect(result.applied).toBe(true); + expect(message.system).toEqual([ + { type: "text", text: "You are a helpful assistant." }, + { type: "text", text: "skills list" }, + ]); + }); + + test("system message with array content - keeps text blocks including extra fields", () => { + const message: Record = { + messages: [ + { role: "user", content: "hi" }, + { + role: "system", + content: [ + { type: "text", text: "skills list", cache_control: { type: "ephemeral" } }, + { type: "image", source: { type: "base64", data: "..." } }, + ], + }, + ], + }; + + const result = rectifySystemMessages(message); + + expect(result.applied).toBe(true); + expect(result.movedCount).toBe(1); + expect(result.extractedValues).toEqual(["skills list"]); + expect(message.system).toEqual([ + { type: "text", text: "skills list", cache_control: { type: "ephemeral" } }, + ]); + }); + + test("multiple system messages - moves all, preserves order", () => { + const message: Record = { + messages: [ + { role: "system", content: "first" }, + { role: "user", content: "hi" }, + { role: "system", content: "second" }, + { role: "assistant", content: "hello" }, + ], + }; + + const result = rectifySystemMessages(message); + + expect(result.applied).toBe(true); + expect(result.movedCount).toBe(2); + expect(result.extractedValues).toEqual(["first", "second"]); + expect(message.messages).toEqual([ + { role: "user", content: "hi" }, + { role: "assistant", content: "hello" }, + ]); + expect(message.system).toEqual([ + { type: "text", text: "first" }, + { type: "text", text: "second" }, + ]); + }); + + test("no system messages - applied=false, messages unchanged", () => { + const original = [ + { role: "user", content: "hi" }, + { role: "assistant", content: "hello" }, + ]; + const message: Record = { + messages: [...original], + }; + + const result = rectifySystemMessages(message); + + expect(result.applied).toBe(false); + expect(result.movedCount).toBe(0); + expect(result.extractedValues).toEqual([]); + expect(message.messages).toEqual(original); + expect(message.system).toBeUndefined(); + }); + + test("messages missing - applied=false", () => { + const message: Record = {}; + + const result = rectifySystemMessages(message); + + expect(result.applied).toBe(false); + expect(result.movedCount).toBe(0); + }); + + test("messages is not an array - applied=false", () => { + const message: Record = { messages: "not-an-array" }; + + const result = rectifySystemMessages(message); + + expect(result.applied).toBe(false); + }); + + test("system message with empty string content - removed from messages without adding empty block", () => { + const message: Record = { + messages: [ + { role: "user", content: "hi" }, + { role: "system", content: "" }, + ], + }; + + const result = rectifySystemMessages(message); + + expect(result.applied).toBe(true); + expect(result.movedCount).toBe(1); + expect(result.extractedValues).toEqual([]); + expect(message.messages).toEqual([{ role: "user", content: "hi" }]); + expect(message.system).toBeUndefined(); + }); + + test("system message with non-string non-array content - removed without merging", () => { + const message: Record = { + messages: [ + { role: "user", content: "hi" }, + { role: "system", content: { unexpected: true } }, + ], + }; + + const result = rectifySystemMessages(message); + + expect(result.applied).toBe(true); + expect(result.movedCount).toBe(1); + expect(result.extractedValues).toEqual([]); + expect(message.system).toBeUndefined(); + }); + + test("top-level system is empty string - no empty leading block", () => { + const message: Record = { + system: "", + messages: [{ role: "system", content: "skills list" }], + }; + + const result = rectifySystemMessages(message); + + expect(result.applied).toBe(true); + expect(message.system).toEqual([{ type: "text", text: "skills list" }]); + }); + + test("top-level system has unknown type - replaced with extracted blocks", () => { + const message: Record = { + system: 42, + messages: [{ role: "system", content: "skills list" }], + }; + + const result = rectifySystemMessages(message); + + expect(result.applied).toBe(true); + expect(message.system).toEqual([{ type: "text", text: "skills list" }]); + }); + + test("mutates messages array in place (same reference)", () => { + const messagesRef: unknown[] = [ + { role: "user", content: "hi" }, + { role: "system", content: "skills list" }, + ]; + const message: Record = { messages: messagesRef }; + + rectifySystemMessages(message); + + expect(message.messages).toBe(messagesRef); + expect(messagesRef).toHaveLength(1); + }); + + test("mutates existing system array in place (same reference)", () => { + const systemRef: unknown[] = [{ type: "text", text: "base prompt" }]; + const message: Record = { + system: systemRef, + messages: [{ role: "system", content: "skills list" }], + }; + + rectifySystemMessages(message); + + expect(message.system).toBe(systemRef); + expect(systemRef).toHaveLength(2); + }); + + test("billing header inside system message is stripped when billing rectifier runs after (forwarder order)", () => { + const message: Record = { + messages: [ + { role: "user", content: "hi" }, + { + role: "system", + content: [ + { type: "text", text: "skills list" }, + { type: "text", text: "x-anthropic-billing-header: cc_version=2.1.172;" }, + ], + }, + ], + }; + + const systemResult = rectifySystemMessages(message); + const billingResult = rectifyBillingHeader(message); + + expect(systemResult.applied).toBe(true); + expect(billingResult.applied).toBe(true); + expect(billingResult.removedCount).toBe(1); + expect(message.system).toEqual([{ type: "text", text: "skills list" }]); + }); + + test("null and non-object entries in messages are preserved", () => { + const message: Record = { + messages: [null, "weird", { role: "system", content: "skills list" }, { role: "user" }], + }; + + const result = rectifySystemMessages(message); + + expect(result.applied).toBe(true); + expect(result.movedCount).toBe(1); + expect(message.messages).toEqual([null, "weird", { role: "user" }]); + }); +}); From 1d2bd1a8360542a02aa7dacc5c2dac7edb8ba29e Mon Sep 17 00:00:00 2001 From: max Date: Thu, 11 Jun 2026 14:33:34 +0800 Subject: [PATCH 2/2] =?UTF-8?q?refactor(proxy):=20=E6=95=B4=E6=B5=81?= =?UTF-8?q?=E5=99=A8=20NOOP=20=E7=BB=93=E6=9E=9C=E6=94=B9=E4=B8=BA?= =?UTF-8?q?=E5=B7=A5=E5=8E=82=E5=87=BD=E6=95=B0=EF=BC=8C=E5=90=88=E5=B9=B6?= =?UTF-8?q?=E9=87=8D=E5=A4=8D=E7=9A=84=20settings=20=E8=AF=BB=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - system-message-rectifier: NOOP_RESULT 常量浅拷贝会共享 extractedValues 数组引用,改为 createNoopResult() 工厂函数,每次返回独立数组 - forwarder: 两个 rectifier 块共用一次 getCachedSystemSettings() 调用,消除冗余 await 回应 PR #1268 的 CodeRabbit 与 Greptile 审查意见 --- src/app/v1/_lib/proxy/forwarder.ts | 9 +++++---- .../v1/_lib/proxy/system-message-rectifier.ts | 16 +++++++++------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/app/v1/_lib/proxy/forwarder.ts b/src/app/v1/_lib/proxy/forwarder.ts index 0bbaded55..2562b1d79 100644 --- a/src/app/v1/_lib/proxy/forwarder.ts +++ b/src/app/v1/_lib/proxy/forwarder.ts @@ -2448,13 +2448,15 @@ export class ProxyForwarder { // Anthropic 供应商级参数覆写(默认 inherit=遵循客户端) // 说明:允许管理员在供应商层面强制覆写 max_tokens 和 thinking.budget_tokens if (provider.providerType === "claude" || provider.providerType === "claude-auth") { + const rectifierSettings = await getCachedSystemSettings(); + // System message rectifier: move role:"system" entries from messages array // into the top-level system field (Claude Code 2.1.172+ with custom model names). // Must run before the billing header rectifier so any billing-header text block // carried inside a role:"system" message lands in `system` before stripping. { - const settings = await getCachedSystemSettings(); - const systemMessageRectifierEnabled = settings.enableSystemMessageRectifier ?? true; + const systemMessageRectifierEnabled = + rectifierSettings.enableSystemMessageRectifier ?? true; if (systemMessageRectifierEnabled) { const systemMessageResult = rectifySystemMessages( session.request.message as Record @@ -2479,8 +2481,7 @@ export class ProxyForwarder { // Billing header rectifier: proactively strip x-anthropic-billing-header from system prompt { - const settings = await getCachedSystemSettings(); - const billingRectifierEnabled = settings.enableBillingHeaderRectifier ?? true; + const billingRectifierEnabled = rectifierSettings.enableBillingHeaderRectifier ?? true; if (billingRectifierEnabled) { const billingResult = rectifyBillingHeader( session.request.message as Record diff --git a/src/app/v1/_lib/proxy/system-message-rectifier.ts b/src/app/v1/_lib/proxy/system-message-rectifier.ts index 05168f696..5876a2bcf 100644 --- a/src/app/v1/_lib/proxy/system-message-rectifier.ts +++ b/src/app/v1/_lib/proxy/system-message-rectifier.ts @@ -22,11 +22,13 @@ export type SystemMessageRectifierResult = { extractedValues: string[]; }; -const NOOP_RESULT: SystemMessageRectifierResult = { - applied: false, - movedCount: 0, - extractedValues: [], -}; +function createNoopResult(): SystemMessageRectifierResult { + return { + applied: false, + movedCount: 0, + extractedValues: [], + }; +} type TextBlock = { type: "text"; text: string; [key: string]: unknown }; @@ -65,7 +67,7 @@ export function rectifySystemMessages( ): SystemMessageRectifierResult { const messages = message.messages; if (!Array.isArray(messages)) { - return { ...NOOP_RESULT }; + return createNoopResult(); } const extractedBlocks: TextBlock[] = []; @@ -89,7 +91,7 @@ export function rectifySystemMessages( } if (movedCount === 0) { - return { ...NOOP_RESULT }; + return createNoopResult(); } // Mutate in place: replace messages array contents