Просмотр исходного кода

feat: Codex 供应商级覆写 reasoning/text/parallel_tool_calls (#404) (#536)

* feat: add Codex provider-level overrides (#404)

* fix: align context1m default and tooltip provider scope

* chore(db): regenerate drizzle migration for codex provider overrides
Ding 1 месяц назад
Родитель
Сommit
16059f89a2

+ 4 - 0
drizzle/0046_woozy_dark_phoenix.sql

@@ -0,0 +1,4 @@
+ALTER TABLE "providers" ADD COLUMN "codex_reasoning_effort_preference" varchar(20);--> statement-breakpoint
+ALTER TABLE "providers" ADD COLUMN "codex_reasoning_summary_preference" varchar(20);--> statement-breakpoint
+ALTER TABLE "providers" ADD COLUMN "codex_text_verbosity_preference" varchar(10);--> statement-breakpoint
+ALTER TABLE "providers" ADD COLUMN "codex_parallel_tool_calls_preference" varchar(10);

+ 2316 - 0
drizzle/meta/0046_snapshot.json

@@ -0,0 +1,2316 @@
+{
+  "id": "7b84bc41-a8ca-48ec-87b3-a6f083d6f967",
+  "prevId": "ceb5d052-434d-4d5a-b8f0-e8f50eb0e2a6",
+  "version": "7",
+  "dialect": "postgresql",
+  "tables": {
+    "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",
+          "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_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
+        },
+        "limit_concurrent_sessions": {
+          "name": "limit_concurrent_sessions",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": false,
+          "default": 0
+        },
+        "provider_group": {
+          "name": "provider_group",
+          "type": "varchar(50)",
+          "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_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
+        },
+        "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
+        },
+        "input_tokens": {
+          "name": "input_tokens",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "output_tokens": {
+          "name": "output_tokens",
+          "type": "integer",
+          "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": "integer",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "cache_read_input_tokens": {
+          "name": "cache_read_input_tokens",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "cache_creation_5m_input_tokens": {
+          "name": "cache_creation_5m_input_tokens",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "cache_creation_1h_input_tokens": {
+          "name": "cache_creation_1h_input_tokens",
+          "type": "integer",
+          "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
+        },
+        "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
+        },
+        "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_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_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_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_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_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": {}
+        }
+      },
+      "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
+        },
+        "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": {}
+        }
+      },
+      "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
+        },
+        "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,
+          "default": "'Asia/Shanghai'"
+        },
+        "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.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
+        },
+        "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
+        },
+        "cost_multiplier": {
+          "name": "cost_multiplier",
+          "type": "numeric(10, 4)",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "'1.0'"
+        },
+        "group_tag": {
+          "name": "group_tag",
+          "type": "varchar(50)",
+          "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
+        },
+        "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"
+        },
+        "join_claude_pool": {
+          "name": "join_claude_pool",
+          "type": "boolean",
+          "primaryKey": false,
+          "notNull": false,
+          "default": 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_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
+        },
+        "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
+        },
+        "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
+        },
+        "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_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": {}
+        }
+      },
+      "foreignKeys": {},
+      "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
+        },
+        "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": {}
+        }
+      },
+      "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'"
+        },
+        "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
+        },
+        "enable_http2": {
+          "name": "enable_http2",
+          "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
+        },
+        "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.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(50)",
+          "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_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
+        },
+        "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"
+        },
+        "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_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"
+      ]
+    },
+    "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": {}
+  }
+}

+ 7 - 0
drizzle/meta/_journal.json

@@ -323,6 +323,13 @@
       "when": 1767521897438,
       "tag": "0045_mushy_human_torch",
       "breakpoints": true
+    },
+    {
+      "idx": 46,
+      "version": "7",
+      "when": 1767535180658,
+      "tag": "0046_woozy_dark_phoenix",
+      "breakpoints": true
     }
   ]
 }

+ 43 - 0
messages/en/settings.json

@@ -1262,6 +1262,49 @@
               "disabled": "Disabled"
             },
             "desc": "Configure 1M context window support. Only affects Sonnet models (claude-sonnet-4-5, claude-sonnet-4). Tiered pricing applies when enabled."
+          },
+          "codexOverrides": {
+            "reasoningEffort": {
+              "label": "Reasoning Effort Override",
+              "help": "Controls how much reasoning effort the model uses before answering. \"inherit\" follows the client request; other values force override reasoning.effort. Note: \"none\" is only supported on GPT-5.1 models; \"xhigh\" is only supported on GPT-5.1-Codex-Max. Using an unsupported value will cause an upstream error.",
+              "options": {
+                "inherit": "No override (follow client)",
+                "minimal": "minimal",
+                "low": "low",
+                "medium": "medium (default)",
+                "high": "high",
+                "xhigh": "xhigh (GPT-5.1-Codex-Max only)",
+                "none": "none (GPT-5.1 only)"
+              }
+            },
+            "reasoningSummary": {
+              "label": "Reasoning Summary Override",
+              "help": "Controls whether the Responses API returns reasoning summaries. \"auto\" returns a condensed summary, \"detailed\" returns a more comprehensive one. \"inherit\" follows the client request.",
+              "options": {
+                "inherit": "No override (follow client)",
+                "auto": "auto",
+                "detailed": "detailed"
+              }
+            },
+            "textVerbosity": {
+              "label": "Text Verbosity Override",
+              "help": "Controls how verbose the model output is. \"low\" is concise, \"high\" is verbose. \"inherit\" follows the client request.",
+              "options": {
+                "inherit": "No override (follow client)",
+                "low": "low",
+                "medium": "medium (default)",
+                "high": "high"
+              }
+            },
+            "parallelToolCalls": {
+              "label": "Parallel Tool Calls Override",
+              "help": "Controls whether parallel tool calls are allowed. \"inherit\" follows the client request. Disabling may reduce tool-call concurrency.",
+              "options": {
+                "inherit": "No override (follow client)",
+                "true": "Force enable",
+                "false": "Force disable"
+              }
+            }
           }
         },
         "rateLimit": {

+ 43 - 0
messages/ja/settings.json

@@ -1153,6 +1153,49 @@
               "disabled": "無効"
             },
             "desc": "1M コンテキストウィンドウのサポートを設定します。Sonnet モデル(claude-sonnet-4-5、claude-sonnet-4)にのみ適用されます。有効時は段階的料金が適用されます。"
+          },
+          "codexOverrides": {
+            "reasoningEffort": {
+              "label": "推論強度オーバーライド",
+              "help": "回答前にモデルが使う推論の強度(推論トークン量)を制御します。「クライアントに従う」はリクエストを変更しません。その他の値は reasoning.effort を強制します。注意: none は GPT-5.1 のみ、xhigh は GPT-5.1-Codex-Max のみ対応で、未対応モデルではエラーになります。",
+              "options": {
+                "inherit": "オーバーライドしない(クライアントに従う)",
+                "minimal": "minimal",
+                "low": "low",
+                "medium": "medium(デフォルト)",
+                "high": "high",
+                "xhigh": "xhigh(GPT-5.1-Codex-Max のみ)",
+                "none": "none(GPT-5.1 のみ)"
+              }
+            },
+            "reasoningSummary": {
+              "label": "推論サマリーオーバーライド",
+              "help": "推論サマリーを返すかどうかを制御します。auto は簡潔、detailed は詳細です。「クライアントに従う」は reasoning.summary を変更しません。",
+              "options": {
+                "inherit": "オーバーライドしない(クライアントに従う)",
+                "auto": "auto",
+                "detailed": "detailed"
+              }
+            },
+            "textVerbosity": {
+              "label": "出力の詳細度オーバーライド",
+              "help": "モデル出力の冗長さを制御します。low は簡潔、high は詳細です。「クライアントに従う」は text.verbosity を変更しません。",
+              "options": {
+                "inherit": "オーバーライドしない(クライアントに従う)",
+                "low": "low",
+                "medium": "medium(デフォルト)",
+                "high": "high"
+              }
+            },
+            "parallelToolCalls": {
+              "label": "並列ツール呼び出しオーバーライド",
+              "help": "並列の tool calls を許可するかどうかを制御します。「クライアントに従う」は parallel_tool_calls を変更しません。無効化すると並列度が下がる可能性があります。",
+              "options": {
+                "inherit": "オーバーライドしない(クライアントに従う)",
+                "true": "強制有効",
+                "false": "強制無効"
+              }
+            }
           }
         },
         "rateLimit": {

+ 43 - 0
messages/ru/settings.json

@@ -1153,6 +1153,49 @@
               "disabled": "Отключено"
             },
             "desc": "Настройка поддержки контекстного окна 1M. Применяется только к моделям Sonnet (claude-sonnet-4-5, claude-sonnet-4). При включении применяется многоуровневая тарификация."
+          },
+          "codexOverrides": {
+            "reasoningEffort": {
+              "label": "Переопределение уровня рассуждений",
+              "help": "Управляет тем, сколько усилий модель тратит на рассуждения перед ответом. \"inherit\" следует запросу клиента, остальные значения принудительно задают reasoning.effort. Примечание: \"none\" поддерживается только моделями GPT-5.1, а \"xhigh\" — только GPT-5.1-Codex-Max. Неподдерживаемые значения приведут к ошибке у апстрима.",
+              "options": {
+                "inherit": "Не переопределять (следовать клиенту)",
+                "minimal": "minimal",
+                "low": "low",
+                "medium": "medium (по умолчанию)",
+                "high": "high",
+                "xhigh": "xhigh (только GPT-5.1-Codex-Max)",
+                "none": "none (только GPT-5.1)"
+              }
+            },
+            "reasoningSummary": {
+              "label": "Переопределение сводки рассуждений",
+              "help": "Управляет тем, возвращает ли Responses API сводку рассуждений. auto — кратко, detailed — подробнее. \"inherit\" следует запросу клиента.",
+              "options": {
+                "inherit": "Не переопределять (следовать клиенту)",
+                "auto": "auto",
+                "detailed": "detailed"
+              }
+            },
+            "textVerbosity": {
+              "label": "Переопределение подробности текста",
+              "help": "Управляет подробностью ответа. low — короче, high — подробнее. \"inherit\" следует запросу клиента.",
+              "options": {
+                "inherit": "Не переопределять (следовать клиенту)",
+                "low": "low",
+                "medium": "medium (по умолчанию)",
+                "high": "high"
+              }
+            },
+            "parallelToolCalls": {
+              "label": "Переопределение параллельных tool calls",
+              "help": "Управляет тем, разрешены ли параллельные вызовы инструментов. \"inherit\" следует запросу клиента. Отключение может снизить параллельность вызовов инструментов.",
+              "options": {
+                "inherit": "Не переопределять (следовать клиенту)",
+                "true": "Принудительно включить",
+                "false": "Принудительно отключить"
+              }
+            }
           }
         },
         "rateLimit": {

+ 43 - 0
messages/zh-CN/settings.json

@@ -766,6 +766,49 @@
               "disabled": "禁用"
             },
             "desc": "配置 1M 上下文窗口支持。仅对 Sonnet 模型生效(claude-sonnet-4-5、claude-sonnet-4)。启用后将应用阶梯定价。"
+          },
+          "codexOverrides": {
+            "reasoningEffort": {
+              "label": "推理等级覆写",
+              "help": "控制模型在输出前用于推理的强度(推理 token 数量)。选择“跟随客户端”表示不改写请求;选择其他值则强制覆写 reasoning.effort。注意:none 仅 GPT-5.1 系列支持;xhigh 仅 GPT-5.1-Codex-Max 支持,模型不支持会返回错误。",
+              "options": {
+                "inherit": "不覆写(跟随客户端)",
+                "minimal": "minimal(更省、更快)",
+                "low": "low",
+                "medium": "medium(默认)",
+                "high": "high(更强推理)",
+                "xhigh": "xhigh(仅 GPT-5.1-Codex-Max)",
+                "none": "none(仅 GPT-5.1)"
+              }
+            },
+            "reasoningSummary": {
+              "label": "推理摘要覆写",
+              "help": "控制是否返回推理摘要。auto 返回精简摘要,detailed 返回更详细摘要;“跟随客户端”不改写 reasoning.summary。",
+              "options": {
+                "inherit": "不覆写(跟随客户端)",
+                "auto": "auto(精简)",
+                "detailed": "detailed(更详细)"
+              }
+            },
+            "textVerbosity": {
+              "label": "输出冗长度覆写",
+              "help": "控制模型输出的详细程度。low 更简洁,high 更详细;“跟随客户端”不改写 text.verbosity。",
+              "options": {
+                "inherit": "不覆写(跟随客户端)",
+                "low": "low(简洁)",
+                "medium": "medium(默认)",
+                "high": "high(更详细)"
+              }
+            },
+            "parallelToolCalls": {
+              "label": "并行工具调用覆写",
+              "help": "控制是否允许并行 tool calls。关闭可能降低工具调用并发能力;“跟随客户端”不改写 parallel_tool_calls。",
+              "options": {
+                "inherit": "不覆写(跟随客户端)",
+                "true": "强制开启",
+                "false": "强制关闭"
+              }
+            }
           }
         },
         "rateLimit": {

+ 43 - 0
messages/zh-TW/settings.json

@@ -1153,6 +1153,49 @@
               "disabled": "停用"
             },
             "desc": "設定 1M 上下文視窗支援。僅對 Sonnet 模型生效(claude-sonnet-4-5、claude-sonnet-4)。啟用後將套用階梯定價。"
+          },
+          "codexOverrides": {
+            "reasoningEffort": {
+              "label": "推理等級覆寫",
+              "help": "控制模型在輸出前用於推理的強度(推理 token 數量)。選擇「跟隨客戶端」表示不改寫請求;選擇其他值則強制覆寫 reasoning.effort。注意:none 僅 GPT-5.1 系列支援;xhigh 僅 GPT-5.1-Codex-Max 支援,模型不支援會返回錯誤。",
+              "options": {
+                "inherit": "不覆寫(跟隨客戶端)",
+                "minimal": "minimal(更省、更快)",
+                "low": "low",
+                "medium": "medium(預設)",
+                "high": "high(更強推理)",
+                "xhigh": "xhigh(僅 GPT-5.1-Codex-Max)",
+                "none": "none(僅 GPT-5.1)"
+              }
+            },
+            "reasoningSummary": {
+              "label": "推理摘要覆寫",
+              "help": "控制是否回傳推理摘要。auto 回傳精簡摘要,detailed 回傳更詳細摘要;「跟隨客戶端」不改寫 reasoning.summary。",
+              "options": {
+                "inherit": "不覆寫(跟隨客戶端)",
+                "auto": "auto(精簡)",
+                "detailed": "detailed(更詳細)"
+              }
+            },
+            "textVerbosity": {
+              "label": "輸出冗長度覆寫",
+              "help": "控制模型輸出的詳細程度。low 較精簡,high 較詳細;「跟隨客戶端」不改寫 text.verbosity。",
+              "options": {
+                "inherit": "不覆寫(跟隨客戶端)",
+                "low": "low(精簡)",
+                "medium": "medium(預設)",
+                "high": "high(更詳細)"
+              }
+            },
+            "parallelToolCalls": {
+              "label": "並行工具呼叫覆寫",
+              "help": "控制是否允許並行 tool calls。關閉可能降低工具呼叫並發能力;「跟隨客戶端」不改寫 parallel_tool_calls。",
+              "options": {
+                "inherit": "不覆寫(跟隨客戶端)",
+                "true": "強制開啟",
+                "false": "強制關閉"
+              }
+            }
           }
         },
         "rateLimit": {

+ 26 - 1
src/actions/providers.ts

@@ -42,7 +42,14 @@ import {
   updateProvider,
 } from "@/repository/provider";
 import type { CacheTtlPreference } from "@/types/cache";
-import type { ProviderDisplay, ProviderType } from "@/types/provider";
+import type {
+  CodexParallelToolCallsPreference,
+  CodexReasoningEffortPreference,
+  CodexReasoningSummaryPreference,
+  CodexTextVerbosityPreference,
+  ProviderDisplay,
+  ProviderType,
+} from "@/types/provider";
 import type { ActionResult } from "./types";
 
 const API_TEST_TIMEOUT_LIMITS = {
@@ -219,6 +226,10 @@ export async function getProviders(): Promise<ProviderDisplay[]> {
         faviconUrl: provider.faviconUrl,
         cacheTtlPreference: provider.cacheTtlPreference,
         context1mPreference: provider.context1mPreference,
+        codexReasoningEffortPreference: provider.codexReasoningEffortPreference,
+        codexReasoningSummaryPreference: provider.codexReasoningSummaryPreference,
+        codexTextVerbosityPreference: provider.codexTextVerbosityPreference,
+        codexParallelToolCallsPreference: provider.codexParallelToolCallsPreference,
         tpm: provider.tpm,
         rpm: provider.rpm,
         rpd: provider.rpd,
@@ -358,6 +369,10 @@ export async function addProvider(data: {
   limit_concurrent_sessions?: number | null;
   cache_ttl_preference?: CacheTtlPreference | null;
   context_1m_preference?: Context1mPreference | null;
+  codex_reasoning_effort_preference?: CodexReasoningEffortPreference | null;
+  codex_reasoning_summary_preference?: CodexReasoningSummaryPreference | null;
+  codex_text_verbosity_preference?: CodexTextVerbosityPreference | null;
+  codex_parallel_tool_calls_preference?: CodexParallelToolCallsPreference | null;
   max_retry_attempts?: number | null;
   circuit_breaker_failure_threshold?: number;
   circuit_breaker_open_duration?: number;
@@ -442,6 +457,12 @@ export async function addProvider(data: {
         validated.request_timeout_non_streaming_ms ??
         PROVIDER_TIMEOUT_DEFAULTS.REQUEST_TIMEOUT_NON_STREAMING_MS,
       cache_ttl_preference: validated.cache_ttl_preference ?? "inherit",
+      context_1m_preference: validated.context_1m_preference ?? "inherit",
+      codex_reasoning_effort_preference: validated.codex_reasoning_effort_preference ?? "inherit",
+      codex_reasoning_summary_preference: validated.codex_reasoning_summary_preference ?? "inherit",
+      codex_text_verbosity_preference: validated.codex_text_verbosity_preference ?? "inherit",
+      codex_parallel_tool_calls_preference:
+        validated.codex_parallel_tool_calls_preference ?? "inherit",
       website_url: validated.website_url ?? null,
       favicon_url: faviconUrl,
       tpm: validated.tpm ?? null,
@@ -515,6 +536,10 @@ export async function editProvider(
     limit_concurrent_sessions?: number | null;
     cache_ttl_preference?: "inherit" | "5m" | "1h";
     context_1m_preference?: Context1mPreference | null;
+    codex_reasoning_effort_preference?: CodexReasoningEffortPreference | null;
+    codex_reasoning_summary_preference?: CodexReasoningSummaryPreference | null;
+    codex_text_verbosity_preference?: CodexTextVerbosityPreference | null;
+    codex_parallel_tool_calls_preference?: CodexParallelToolCallsPreference | null;
     max_retry_attempts?: number | null;
     circuit_breaker_failure_threshold?: number;
     circuit_breaker_open_duration?: number;

+ 219 - 4
src/app/[locale]/settings/providers/_components/forms/provider-form.tsx

@@ -1,5 +1,5 @@
 "use client";
-import { ChevronDown } from "lucide-react";
+import { ChevronDown, Info } from "lucide-react";
 import { useTranslations } from "next-intl";
 import { useEffect, useRef, useState, useTransition } from "react";
 import { toast } from "sonner";
@@ -31,6 +31,7 @@ import {
 } from "@/components/ui/select";
 import { Switch } from "@/components/ui/switch";
 import { TagInput } from "@/components/ui/tag-input";
+import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
 import { PROVIDER_DEFAULTS, PROVIDER_TIMEOUT_DEFAULTS } from "@/lib/constants/provider.constants";
 import type { Context1mPreference } from "@/lib/special-attributes";
 import {
@@ -39,7 +40,15 @@ import {
   validateNumericField,
   validatePositiveDecimalField,
 } from "@/lib/utils/validation";
-import type { McpPassthroughType, ProviderDisplay, ProviderType } from "@/types/provider";
+import type {
+  CodexParallelToolCallsPreference,
+  CodexReasoningEffortPreference,
+  CodexReasoningSummaryPreference,
+  CodexTextVerbosityPreference,
+  McpPassthroughType,
+  ProviderDisplay,
+  ProviderType,
+} from "@/types/provider";
 import { ModelMultiSelect } from "../model-multi-select";
 import { ModelRedirectEditor } from "../model-redirect-editor";
 import { ApiTestButton } from "./api-test-button";
@@ -56,6 +65,36 @@ interface ProviderFormProps {
   enableMultiProviderTypes: boolean;
 }
 
+function FieldLabelWithTooltip({
+  label,
+  tooltip,
+  htmlFor,
+}: {
+  label: string;
+  tooltip: string;
+  htmlFor?: string;
+}) {
+  return (
+    <div className="flex items-center gap-2">
+      <Label htmlFor={htmlFor}>{label}</Label>
+      <Tooltip>
+        <TooltipTrigger asChild>
+          <button
+            type="button"
+            className="text-muted-foreground hover:text-foreground transition-colors"
+            aria-label={`${label} - help`}
+          >
+            <Info className="h-4 w-4" />
+          </button>
+        </TooltipTrigger>
+        <TooltipContent side="top" className="max-w-xs">
+          <p className="text-sm leading-relaxed">{tooltip}</p>
+        </TooltipContent>
+      </Tooltip>
+    </div>
+  );
+}
+
 export function ProviderForm({
   mode,
   onSuccess,
@@ -137,6 +176,24 @@ export function ProviderForm({
     "inherit" | "force_enable" | "disabled"
   >((sourceProvider?.context1mPreference as "inherit" | "force_enable" | "disabled") ?? "inherit");
 
+  // Codex(Responses API)供应商级参数覆写(仅对 Codex 类型供应商有效)
+  const [codexReasoningEffortPreference, setCodexReasoningEffortPreference] =
+    useState<CodexReasoningEffortPreference>(
+      sourceProvider?.codexReasoningEffortPreference ?? "inherit"
+    );
+  const [codexReasoningSummaryPreference, setCodexReasoningSummaryPreference] =
+    useState<CodexReasoningSummaryPreference>(
+      sourceProvider?.codexReasoningSummaryPreference ?? "inherit"
+    );
+  const [codexTextVerbosityPreference, setCodexTextVerbosityPreference] =
+    useState<CodexTextVerbosityPreference>(
+      sourceProvider?.codexTextVerbosityPreference ?? "inherit"
+    );
+  const [codexParallelToolCallsPreference, setCodexParallelToolCallsPreference] =
+    useState<CodexParallelToolCallsPreference>(
+      sourceProvider?.codexParallelToolCallsPreference ?? "inherit"
+    );
+
   // 熔断器配置(以分钟为单位显示,提交时转换为毫秒)
   // 允许 undefined,用户可以清空输入框,提交时使用默认值
   const [failureThreshold, setFailureThreshold] = useState<number | undefined>(
@@ -344,6 +401,10 @@ export function ProviderForm({
             limit_concurrent_sessions?: number | null;
             cache_ttl_preference?: "inherit" | "5m" | "1h";
             context_1m_preference?: Context1mPreference | null;
+            codex_reasoning_effort_preference?: CodexReasoningEffortPreference | null;
+            codex_reasoning_summary_preference?: CodexReasoningSummaryPreference | null;
+            codex_text_verbosity_preference?: CodexTextVerbosityPreference | null;
+            codex_parallel_tool_calls_preference?: CodexParallelToolCallsPreference | null;
             max_retry_attempts?: number | null;
             circuit_breaker_failure_threshold?: number;
             circuit_breaker_open_duration?: number;
@@ -383,6 +444,10 @@ export function ProviderForm({
             limit_concurrent_sessions: limitConcurrentSessions ?? 0,
             cache_ttl_preference: cacheTtlPreference,
             context_1m_preference: context1mPreference,
+            codex_reasoning_effort_preference: codexReasoningEffortPreference,
+            codex_reasoning_summary_preference: codexReasoningSummaryPreference,
+            codex_text_verbosity_preference: codexTextVerbosityPreference,
+            codex_parallel_tool_calls_preference: codexParallelToolCallsPreference,
             max_retry_attempts: maxRetryAttempts,
             circuit_breaker_failure_threshold: failureThreshold ?? 5,
             circuit_breaker_open_duration: openDurationMinutes
@@ -444,6 +509,10 @@ export function ProviderForm({
             limit_concurrent_sessions: limitConcurrentSessions ?? 0,
             cache_ttl_preference: cacheTtlPreference,
             context_1m_preference: context1mPreference,
+            codex_reasoning_effort_preference: codexReasoningEffortPreference,
+            codex_reasoning_summary_preference: codexReasoningSummaryPreference,
+            codex_text_verbosity_preference: codexTextVerbosityPreference,
+            codex_parallel_tool_calls_preference: codexParallelToolCallsPreference,
             max_retry_attempts: maxRetryAttempts,
             circuit_breaker_failure_threshold: failureThreshold ?? 5,
             circuit_breaker_open_duration: openDurationMinutes
@@ -528,7 +597,7 @@ export function ProviderForm({
   };
 
   return (
-    <>
+    <TooltipProvider>
       <DialogHeader className="flex-shrink-0">
         <DialogTitle>{isEdit ? t("title.edit") : t("title.create")}</DialogTitle>
       </DialogHeader>
@@ -996,6 +1065,152 @@ export function ProviderForm({
                       </p>
                     </div>
                   )}
+
+                  {/* Codex 参数覆写 - 仅 Codex 类型供应商显示 */}
+                  {providerType === "codex" && (
+                    <div className="space-y-4">
+                      <div className="space-y-2">
+                        <FieldLabelWithTooltip
+                          label={t("sections.routing.codexOverrides.reasoningEffort.label")}
+                          tooltip={t("sections.routing.codexOverrides.reasoningEffort.help")}
+                        />
+                        <Select
+                          value={codexReasoningEffortPreference}
+                          onValueChange={(val) =>
+                            setCodexReasoningEffortPreference(val as CodexReasoningEffortPreference)
+                          }
+                          disabled={isPending}
+                        >
+                          <SelectTrigger className="w-full">
+                            <SelectValue placeholder="inherit" />
+                          </SelectTrigger>
+                          <SelectContent>
+                            <SelectItem value="inherit">
+                              {t("sections.routing.codexOverrides.reasoningEffort.options.inherit")}
+                            </SelectItem>
+                            <SelectItem value="minimal">
+                              {t("sections.routing.codexOverrides.reasoningEffort.options.minimal")}
+                            </SelectItem>
+                            <SelectItem value="low">
+                              {t("sections.routing.codexOverrides.reasoningEffort.options.low")}
+                            </SelectItem>
+                            <SelectItem value="medium">
+                              {t("sections.routing.codexOverrides.reasoningEffort.options.medium")}
+                            </SelectItem>
+                            <SelectItem value="high">
+                              {t("sections.routing.codexOverrides.reasoningEffort.options.high")}
+                            </SelectItem>
+                            <SelectItem value="xhigh">
+                              {t("sections.routing.codexOverrides.reasoningEffort.options.xhigh")}
+                            </SelectItem>
+                            <SelectItem value="none">
+                              {t("sections.routing.codexOverrides.reasoningEffort.options.none")}
+                            </SelectItem>
+                          </SelectContent>
+                        </Select>
+                      </div>
+
+                      <div className="space-y-2">
+                        <FieldLabelWithTooltip
+                          label={t("sections.routing.codexOverrides.reasoningSummary.label")}
+                          tooltip={t("sections.routing.codexOverrides.reasoningSummary.help")}
+                        />
+                        <Select
+                          value={codexReasoningSummaryPreference}
+                          onValueChange={(val) =>
+                            setCodexReasoningSummaryPreference(
+                              val as CodexReasoningSummaryPreference
+                            )
+                          }
+                          disabled={isPending}
+                        >
+                          <SelectTrigger className="w-full">
+                            <SelectValue placeholder="inherit" />
+                          </SelectTrigger>
+                          <SelectContent>
+                            <SelectItem value="inherit">
+                              {t(
+                                "sections.routing.codexOverrides.reasoningSummary.options.inherit"
+                              )}
+                            </SelectItem>
+                            <SelectItem value="auto">
+                              {t("sections.routing.codexOverrides.reasoningSummary.options.auto")}
+                            </SelectItem>
+                            <SelectItem value="detailed">
+                              {t(
+                                "sections.routing.codexOverrides.reasoningSummary.options.detailed"
+                              )}
+                            </SelectItem>
+                          </SelectContent>
+                        </Select>
+                      </div>
+
+                      <div className="space-y-2">
+                        <FieldLabelWithTooltip
+                          label={t("sections.routing.codexOverrides.textVerbosity.label")}
+                          tooltip={t("sections.routing.codexOverrides.textVerbosity.help")}
+                        />
+                        <Select
+                          value={codexTextVerbosityPreference}
+                          onValueChange={(val) =>
+                            setCodexTextVerbosityPreference(val as CodexTextVerbosityPreference)
+                          }
+                          disabled={isPending}
+                        >
+                          <SelectTrigger className="w-full">
+                            <SelectValue placeholder="inherit" />
+                          </SelectTrigger>
+                          <SelectContent>
+                            <SelectItem value="inherit">
+                              {t("sections.routing.codexOverrides.textVerbosity.options.inherit")}
+                            </SelectItem>
+                            <SelectItem value="low">
+                              {t("sections.routing.codexOverrides.textVerbosity.options.low")}
+                            </SelectItem>
+                            <SelectItem value="medium">
+                              {t("sections.routing.codexOverrides.textVerbosity.options.medium")}
+                            </SelectItem>
+                            <SelectItem value="high">
+                              {t("sections.routing.codexOverrides.textVerbosity.options.high")}
+                            </SelectItem>
+                          </SelectContent>
+                        </Select>
+                      </div>
+
+                      <div className="space-y-2">
+                        <FieldLabelWithTooltip
+                          label={t("sections.routing.codexOverrides.parallelToolCalls.label")}
+                          tooltip={t("sections.routing.codexOverrides.parallelToolCalls.help")}
+                        />
+                        <Select
+                          value={codexParallelToolCallsPreference}
+                          onValueChange={(val) =>
+                            setCodexParallelToolCallsPreference(
+                              val as CodexParallelToolCallsPreference
+                            )
+                          }
+                          disabled={isPending}
+                        >
+                          <SelectTrigger className="w-full">
+                            <SelectValue placeholder="inherit" />
+                          </SelectTrigger>
+                          <SelectContent>
+                            <SelectItem value="inherit">
+                              {t(
+                                "sections.routing.codexOverrides.parallelToolCalls.options.inherit"
+                              )}
+                            </SelectItem>
+                            <SelectItem value="true">
+                              {t("sections.routing.codexOverrides.parallelToolCalls.options.true")}
+                            </SelectItem>
+                            <SelectItem value="false">
+                              {t("sections.routing.codexOverrides.parallelToolCalls.options.false")}
+                            </SelectItem>
+                          </SelectContent>
+                        </Select>
+                      </div>
+                    </div>
+                  )}
                 </div>
               </div>
             </CollapsibleContent>
@@ -1879,6 +2094,6 @@ export function ProviderForm({
           )}
         </DialogFooter>
       </form>
-    </>
+    </TooltipProvider>
   );
 }

+ 4 - 1
src/app/v1/_lib/codex/utils/request-sanitizer.ts

@@ -120,7 +120,10 @@ export async function sanitizeCodexRequest(
   // 如果客户端未指定 stream,则保持 undefined,由上游 API 决定默认行为
   // 参考:https://github.com/ding113/claude-code-hub/issues/368
   output.store = false; // Codex 不存储对话历史
-  output.parallel_tool_calls = true; // Codex 支持并行工具调用
+  // 并行工具调用:默认值为 true,但应允许客户端显式关闭
+  if (typeof output.parallel_tool_calls !== "boolean") {
+    output.parallel_tool_calls = true;
+  }
 
   logger.info("[CodexSanitizer] Request sanitized successfully", {
     model,

+ 3 - 1
src/app/v1/_lib/converters/openai-to-codex/request.ts

@@ -126,7 +126,9 @@ export function transformOpenAIRequestToCodex(
     model,
     stream: true, // 强制 stream: true
     store: false, // 强制 store: false
-    parallel_tool_calls: true, // 强制启用并行工具调用
+    // 并行工具调用:默认 true,但应允许客户端显式关闭
+    parallel_tool_calls:
+      typeof req.parallel_tool_calls === "boolean" ? req.parallel_tool_calls : true,
     include: ["reasoning.encrypted_content"], // 包含推理内容
     input: [],
   };

+ 8 - 0
src/app/v1/_lib/proxy/forwarder.ts

@@ -9,6 +9,7 @@ import {
   recordFailure,
   recordSuccess,
 } from "@/lib/circuit-breaker";
+import { applyCodexProviderOverrides } from "@/lib/codex/provider-overrides";
 import { isHttp2Enabled } from "@/lib/config";
 import { getEnvConfig } from "@/lib/config/env.schema";
 import { PROVIDER_DEFAULTS, PROVIDER_LIMITS } from "@/lib/constants/provider.constants";
@@ -990,6 +991,13 @@ export class ProxyForwarder {
             });
           }
         }
+
+        // Codex 供应商级参数覆写(默认 inherit=遵循客户端)
+        // 说明:即使官方客户端跳过清洗,也允许管理员在供应商层面强制覆写关键参数
+        session.request.message = applyCodexProviderOverrides(
+          provider,
+          session.request.message as Record<string, unknown>
+        );
       }
 
       if (

+ 8 - 0
src/drizzle/schema.ts

@@ -243,6 +243,14 @@ export const providers = pgTable('providers', {
   // - 'disabled': 禁用 1M 上下文,即使客户端请求也不启用
   context1mPreference: varchar('context_1m_preference', { length: 20 }),
 
+  // Codex(Responses API)参数覆写(仅对 Codex 类型供应商有效)
+  // - 'inherit' 或 null: 遵循客户端请求
+  // - 其他值: 强制覆写对应请求体字段
+  codexReasoningEffortPreference: varchar('codex_reasoning_effort_preference', { length: 20 }),
+  codexReasoningSummaryPreference: varchar('codex_reasoning_summary_preference', { length: 20 }),
+  codexTextVerbosityPreference: varchar('codex_text_verbosity_preference', { length: 10 }),
+  codexParallelToolCallsPreference: varchar('codex_parallel_tool_calls_preference', { length: 10 }),
+
   // 废弃(保留向后兼容,但不再使用)
   tpm: integer('tpm').default(0),
   rpm: integer('rpm').default(0),

+ 90 - 0
src/lib/codex/provider-overrides.ts

@@ -0,0 +1,90 @@
+import type {
+  CodexParallelToolCallsPreference,
+  CodexReasoningEffortPreference,
+  CodexReasoningSummaryPreference,
+  CodexTextVerbosityPreference,
+} from "@/types/provider";
+
+type CodexProviderOverrideConfig = {
+  providerType?: string;
+  codexReasoningEffortPreference?: CodexReasoningEffortPreference | null;
+  codexReasoningSummaryPreference?: CodexReasoningSummaryPreference | null;
+  codexTextVerbosityPreference?: CodexTextVerbosityPreference | null;
+  codexParallelToolCallsPreference?: CodexParallelToolCallsPreference | null;
+};
+
+function isPlainObject(value: unknown): value is Record<string, unknown> {
+  return typeof value === "object" && value !== null && !Array.isArray(value);
+}
+
+function normalizeStringPreference(value: string | null | undefined): string | null {
+  if (!value || value === "inherit") return null;
+  return value;
+}
+
+function normalizeParallelToolCallsPreference(
+  value: CodexParallelToolCallsPreference | null | undefined
+): boolean | null {
+  if (!value || value === "inherit") return null;
+  return value === "true";
+}
+
+/**
+ * 根据供应商配置对 Codex(Responses API)请求体进行覆写。
+ *
+ * 约定:
+ * - providerType !== "codex" 时不做任何处理
+ * - 偏好值为 null/undefined/"inherit" 表示“遵循客户端”
+ * - 覆写仅影响以下字段:
+ *   - parallel_tool_calls
+ *   - reasoning.effort / reasoning.summary
+ *   - text.verbosity
+ */
+export function applyCodexProviderOverrides(
+  provider: CodexProviderOverrideConfig,
+  request: Record<string, unknown>
+): Record<string, unknown> {
+  if (provider.providerType !== "codex") {
+    return request;
+  }
+
+  let output: Record<string, unknown> = request;
+  const ensureCloned = () => {
+    if (output === request) {
+      output = { ...request };
+    }
+  };
+
+  const parallelToolCalls = normalizeParallelToolCallsPreference(
+    provider.codexParallelToolCallsPreference
+  );
+  if (parallelToolCalls !== null) {
+    ensureCloned();
+    output.parallel_tool_calls = parallelToolCalls;
+  }
+
+  const reasoningEffort = normalizeStringPreference(provider.codexReasoningEffortPreference);
+  const reasoningSummary = normalizeStringPreference(provider.codexReasoningSummaryPreference);
+  if (reasoningEffort !== null || reasoningSummary !== null) {
+    ensureCloned();
+    const existingReasoning = isPlainObject(output.reasoning) ? output.reasoning : {};
+    const nextReasoning: Record<string, unknown> = { ...existingReasoning };
+    if (reasoningEffort !== null) {
+      nextReasoning.effort = reasoningEffort;
+    }
+    if (reasoningSummary !== null) {
+      nextReasoning.summary = reasoningSummary;
+    }
+    output.reasoning = nextReasoning;
+  }
+
+  const textVerbosity = normalizeStringPreference(provider.codexTextVerbosityPreference);
+  if (textVerbosity !== null) {
+    ensureCloned();
+    const existingText = isPlainObject(output.text) ? output.text : {};
+    const nextText: Record<string, unknown> = { ...existingText, verbosity: textVerbosity };
+    output.text = nextText;
+  }
+
+  return output;
+}

+ 25 - 0
src/lib/validation/schemas.ts

@@ -10,6 +10,20 @@ import { CURRENCY_CONFIG } from "@/lib/utils/currency";
 const CACHE_TTL_PREFERENCE = z.enum(["inherit", "5m", "1h"]);
 const CONTEXT_1M_PREFERENCE = z.enum(["inherit", "force_enable", "disabled"]);
 
+// Codex(Responses API)供应商级覆写偏好
+const CODEX_REASONING_EFFORT_PREFERENCE = z.enum([
+  "inherit",
+  "none",
+  "minimal",
+  "low",
+  "medium",
+  "high",
+  "xhigh",
+]);
+const CODEX_REASONING_SUMMARY_PREFERENCE = z.enum(["inherit", "auto", "detailed"]);
+const CODEX_TEXT_VERBOSITY_PREFERENCE = z.enum(["inherit", "low", "medium", "high"]);
+const CODEX_PARALLEL_TOOL_CALLS_PREFERENCE = z.enum(["inherit", "true", "false"]);
+
 /**
  * 用户创建数据验证schema
  */
@@ -412,6 +426,13 @@ export const CreateProviderSchema = z.object({
     .default(0),
   cache_ttl_preference: CACHE_TTL_PREFERENCE.optional().default("inherit"),
   context_1m_preference: CONTEXT_1M_PREFERENCE.nullable().optional(),
+  codex_reasoning_effort_preference:
+    CODEX_REASONING_EFFORT_PREFERENCE.optional().default("inherit"),
+  codex_reasoning_summary_preference:
+    CODEX_REASONING_SUMMARY_PREFERENCE.optional().default("inherit"),
+  codex_text_verbosity_preference: CODEX_TEXT_VERBOSITY_PREFERENCE.optional().default("inherit"),
+  codex_parallel_tool_calls_preference:
+    CODEX_PARALLEL_TOOL_CALLS_PREFERENCE.optional().default("inherit"),
   max_retry_attempts: z.coerce
     .number()
     .int("重试次数必须是整数")
@@ -582,6 +603,10 @@ export const UpdateProviderSchema = z
       .optional(),
     cache_ttl_preference: CACHE_TTL_PREFERENCE.optional(),
     context_1m_preference: CONTEXT_1M_PREFERENCE.nullable().optional(),
+    codex_reasoning_effort_preference: CODEX_REASONING_EFFORT_PREFERENCE.optional(),
+    codex_reasoning_summary_preference: CODEX_REASONING_SUMMARY_PREFERENCE.optional(),
+    codex_text_verbosity_preference: CODEX_TEXT_VERBOSITY_PREFERENCE.optional(),
+    codex_parallel_tool_calls_preference: CODEX_PARALLEL_TOOL_CALLS_PREFERENCE.optional(),
     max_retry_attempts: z.coerce
       .number()
       .int("重试次数必须是整数")

+ 4 - 0
src/repository/_shared/transformers.ts

@@ -104,6 +104,10 @@ export function toProvider(dbProvider: any): Provider {
     faviconUrl: dbProvider?.faviconUrl ?? null,
     cacheTtlPreference: dbProvider?.cacheTtlPreference ?? null,
     context1mPreference: dbProvider?.context1mPreference ?? null,
+    codexReasoningEffortPreference: dbProvider?.codexReasoningEffortPreference ?? null,
+    codexReasoningSummaryPreference: dbProvider?.codexReasoningSummaryPreference ?? null,
+    codexTextVerbosityPreference: dbProvider?.codexTextVerbosityPreference ?? null,
+    codexParallelToolCallsPreference: dbProvider?.codexParallelToolCallsPreference ?? null,
     tpm: dbProvider?.tpm ?? null,
     rpm: dbProvider?.rpm ?? null,
     rpd: dbProvider?.rpd ?? null,

+ 34 - 0
src/repository/provider.ts

@@ -53,6 +53,10 @@ export async function createProvider(providerData: CreateProviderData): Promise<
     faviconUrl: providerData.favicon_url ?? null,
     cacheTtlPreference: providerData.cache_ttl_preference ?? null,
     context1mPreference: providerData.context_1m_preference ?? null,
+    codexReasoningEffortPreference: providerData.codex_reasoning_effort_preference ?? null,
+    codexReasoningSummaryPreference: providerData.codex_reasoning_summary_preference ?? null,
+    codexTextVerbosityPreference: providerData.codex_text_verbosity_preference ?? null,
+    codexParallelToolCallsPreference: providerData.codex_parallel_tool_calls_preference ?? null,
     tpm: providerData.tpm,
     rpm: providerData.rpm,
     rpd: providerData.rpd,
@@ -99,6 +103,10 @@ export async function createProvider(providerData: CreateProviderData): Promise<
     faviconUrl: providers.faviconUrl,
     cacheTtlPreference: providers.cacheTtlPreference,
     context1mPreference: providers.context1mPreference,
+    codexReasoningEffortPreference: providers.codexReasoningEffortPreference,
+    codexReasoningSummaryPreference: providers.codexReasoningSummaryPreference,
+    codexTextVerbosityPreference: providers.codexTextVerbosityPreference,
+    codexParallelToolCallsPreference: providers.codexParallelToolCallsPreference,
     tpm: providers.tpm,
     rpm: providers.rpm,
     rpd: providers.rpd,
@@ -156,6 +164,10 @@ export async function findProviderList(
       faviconUrl: providers.faviconUrl,
       cacheTtlPreference: providers.cacheTtlPreference,
       context1mPreference: providers.context1mPreference,
+      codexReasoningEffortPreference: providers.codexReasoningEffortPreference,
+      codexReasoningSummaryPreference: providers.codexReasoningSummaryPreference,
+      codexTextVerbosityPreference: providers.codexTextVerbosityPreference,
+      codexParallelToolCallsPreference: providers.codexParallelToolCallsPreference,
       tpm: providers.tpm,
       rpm: providers.rpm,
       rpd: providers.rpd,
@@ -224,6 +236,10 @@ export async function findAllProviders(): Promise<Provider[]> {
       faviconUrl: providers.faviconUrl,
       cacheTtlPreference: providers.cacheTtlPreference,
       context1mPreference: providers.context1mPreference,
+      codexReasoningEffortPreference: providers.codexReasoningEffortPreference,
+      codexReasoningSummaryPreference: providers.codexReasoningSummaryPreference,
+      codexTextVerbosityPreference: providers.codexTextVerbosityPreference,
+      codexParallelToolCallsPreference: providers.codexParallelToolCallsPreference,
       tpm: providers.tpm,
       rpm: providers.rpm,
       rpd: providers.rpd,
@@ -286,6 +302,10 @@ export async function findProviderById(id: number): Promise<Provider | null> {
       faviconUrl: providers.faviconUrl,
       cacheTtlPreference: providers.cacheTtlPreference,
       context1mPreference: providers.context1mPreference,
+      codexReasoningEffortPreference: providers.codexReasoningEffortPreference,
+      codexReasoningSummaryPreference: providers.codexReasoningSummaryPreference,
+      codexTextVerbosityPreference: providers.codexTextVerbosityPreference,
+      codexParallelToolCallsPreference: providers.codexParallelToolCallsPreference,
       tpm: providers.tpm,
       rpm: providers.rpm,
       rpd: providers.rpd,
@@ -382,6 +402,16 @@ export async function updateProvider(
     dbData.cacheTtlPreference = providerData.cache_ttl_preference ?? null;
   if (providerData.context_1m_preference !== undefined)
     dbData.context1mPreference = providerData.context_1m_preference ?? null;
+  if (providerData.codex_reasoning_effort_preference !== undefined)
+    dbData.codexReasoningEffortPreference = providerData.codex_reasoning_effort_preference ?? null;
+  if (providerData.codex_reasoning_summary_preference !== undefined)
+    dbData.codexReasoningSummaryPreference =
+      providerData.codex_reasoning_summary_preference ?? null;
+  if (providerData.codex_text_verbosity_preference !== undefined)
+    dbData.codexTextVerbosityPreference = providerData.codex_text_verbosity_preference ?? null;
+  if (providerData.codex_parallel_tool_calls_preference !== undefined)
+    dbData.codexParallelToolCallsPreference =
+      providerData.codex_parallel_tool_calls_preference ?? null;
   if (providerData.tpm !== undefined) dbData.tpm = providerData.tpm;
   if (providerData.rpm !== undefined) dbData.rpm = providerData.rpm;
   if (providerData.rpd !== undefined) dbData.rpd = providerData.rpd;
@@ -431,6 +461,10 @@ export async function updateProvider(
       faviconUrl: providers.faviconUrl,
       cacheTtlPreference: providers.cacheTtlPreference,
       context1mPreference: providers.context1mPreference,
+      codexReasoningEffortPreference: providers.codexReasoningEffortPreference,
+      codexReasoningSummaryPreference: providers.codexReasoningSummaryPreference,
+      codexTextVerbosityPreference: providers.codexTextVerbosityPreference,
+      codexParallelToolCallsPreference: providers.codexParallelToolCallsPreference,
       tpm: providers.tpm,
       rpm: providers.rpm,
       rpd: providers.rpd,

+ 37 - 0
src/types/provider.ts

@@ -11,6 +11,25 @@ export type ProviderType =
   | "gemini-cli"
   | "openai-compatible";
 
+// Codex(Responses API)请求参数覆写偏好
+// - "inherit": 遵循客户端请求(默认)
+// - 其他值: 强制覆写请求体字段
+export type CodexReasoningEffortPreference =
+  | "inherit"
+  | "none"
+  | "minimal"
+  | "low"
+  | "medium"
+  | "high"
+  | "xhigh";
+
+export type CodexReasoningSummaryPreference = "inherit" | "auto" | "detailed";
+
+export type CodexTextVerbosityPreference = "inherit" | "low" | "medium" | "high";
+
+// 由于 Select 的 value 需要是字符串,这里用 "true"/"false" 表示布尔值
+export type CodexParallelToolCallsPreference = "inherit" | "true" | "false";
+
 // Codex Instructions 策略枚举
 export type CodexInstructionsStrategy = "auto" | "force_official" | "keep_original";
 
@@ -101,6 +120,12 @@ export interface Provider {
   // 1M Context Window 偏好配置(仅对 Anthropic 类型供应商有效)
   context1mPreference: Context1mPreference | null;
 
+  // Codex(Responses API)参数覆写(仅对 Codex 类型供应商有效)
+  codexReasoningEffortPreference: CodexReasoningEffortPreference | null;
+  codexReasoningSummaryPreference: CodexReasoningSummaryPreference | null;
+  codexTextVerbosityPreference: CodexTextVerbosityPreference | null;
+  codexParallelToolCallsPreference: CodexParallelToolCallsPreference | null;
+
   // 废弃(保留向后兼容,但不再使用)
   // TPM (Tokens Per Minute): 每分钟可处理的文本总量
   tpm: number | null;
@@ -169,6 +194,10 @@ export interface ProviderDisplay {
   faviconUrl: string | null;
   cacheTtlPreference: CacheTtlPreference | null;
   context1mPreference: Context1mPreference | null;
+  codexReasoningEffortPreference: CodexReasoningEffortPreference | null;
+  codexReasoningSummaryPreference: CodexReasoningSummaryPreference | null;
+  codexTextVerbosityPreference: CodexTextVerbosityPreference | null;
+  codexParallelToolCallsPreference: CodexParallelToolCallsPreference | null;
   // 废弃字段(保留向后兼容)
   tpm: number | null;
   rpm: number | null;
@@ -237,6 +266,10 @@ export interface CreateProviderData {
   favicon_url?: string | null;
   cache_ttl_preference?: CacheTtlPreference | null;
   context_1m_preference?: Context1mPreference | null;
+  codex_reasoning_effort_preference?: CodexReasoningEffortPreference | null;
+  codex_reasoning_summary_preference?: CodexReasoningSummaryPreference | null;
+  codex_text_verbosity_preference?: CodexTextVerbosityPreference | null;
+  codex_parallel_tool_calls_preference?: CodexParallelToolCallsPreference | null;
 
   // 废弃字段(保留向后兼容)
   // TPM (Tokens Per Minute): 每分钟可处理的文本总量
@@ -303,6 +336,10 @@ export interface UpdateProviderData {
   favicon_url?: string | null;
   cache_ttl_preference?: CacheTtlPreference | null;
   context_1m_preference?: Context1mPreference | null;
+  codex_reasoning_effort_preference?: CodexReasoningEffortPreference | null;
+  codex_reasoning_summary_preference?: CodexReasoningSummaryPreference | null;
+  codex_text_verbosity_preference?: CodexTextVerbosityPreference | null;
+  codex_parallel_tool_calls_preference?: CodexParallelToolCallsPreference | null;
 
   // 废弃字段(保留向后兼容)
   // TPM (Tokens Per Minute): 每分钟可处理的文本总量

+ 124 - 0
tests/unit/proxy/codex-provider-overrides.test.ts

@@ -0,0 +1,124 @@
+import { describe, expect, it } from "vitest";
+import { applyCodexProviderOverrides } from "@/lib/codex/provider-overrides";
+
+describe("Codex 供应商级参数覆写", () => {
+  it("当 providerType 不是 codex 时,应直接返回原对象且不做任何处理", () => {
+    const provider = {
+      providerType: "claude",
+      codexReasoningEffortPreference: "high",
+      codexParallelToolCallsPreference: "false",
+    };
+
+    const input: Record<string, unknown> = {
+      model: "gpt-5-codex",
+      input: [],
+      parallel_tool_calls: true,
+      reasoning: { effort: "low", summary: "auto" },
+    };
+
+    const output = applyCodexProviderOverrides(provider as any, input);
+    expect(output).toBe(input);
+    expect(output).toEqual(input);
+  });
+
+  it("当所有偏好均为 inherit/null 时,应保持请求不变", () => {
+    const provider = {
+      providerType: "codex",
+      codexReasoningEffortPreference: null,
+      codexReasoningSummaryPreference: null,
+      codexTextVerbosityPreference: null,
+      codexParallelToolCallsPreference: null,
+    };
+
+    const input: Record<string, unknown> = {
+      model: "gpt-5-codex",
+      input: [],
+      parallel_tool_calls: false,
+      reasoning: { effort: "low", summary: "auto" },
+      text: { verbosity: "medium" },
+    };
+    const snapshot = structuredClone(input);
+
+    const output = applyCodexProviderOverrides(provider as any, input);
+
+    expect(output).toEqual(snapshot);
+    expect(input).toEqual(snapshot);
+  });
+
+  it("当偏好值为字符串 inherit 时,应视为不覆写", () => {
+    const provider = {
+      providerType: "codex",
+      codexReasoningEffortPreference: "inherit",
+      codexReasoningSummaryPreference: "inherit",
+      codexTextVerbosityPreference: "inherit",
+      codexParallelToolCallsPreference: "inherit",
+    };
+
+    const input: Record<string, unknown> = {
+      model: "gpt-5-codex",
+      input: [],
+      parallel_tool_calls: false,
+      reasoning: { effort: "low", summary: "auto" },
+      text: { verbosity: "medium", other: "keep" },
+    };
+    const snapshot = structuredClone(input);
+
+    const output = applyCodexProviderOverrides(provider as any, input);
+
+    expect(output).toEqual(snapshot);
+    expect(input).toEqual(snapshot);
+  });
+
+  it("当强制 parallel_tool_calls 时,应覆写为对应布尔值", () => {
+    const provider = {
+      providerType: "codex",
+      codexParallelToolCallsPreference: "false",
+    };
+
+    const input: Record<string, unknown> = {
+      model: "gpt-5-codex",
+      input: [],
+      parallel_tool_calls: true,
+    };
+
+    const output = applyCodexProviderOverrides(provider as any, input);
+    expect(output.parallel_tool_calls).toBe(false);
+    expect(input.parallel_tool_calls).toBe(true);
+  });
+
+  it("当强制 reasoning.effort/summary 时,应覆写并保留 reasoning 的其他字段", () => {
+    const provider = {
+      providerType: "codex",
+      codexReasoningEffortPreference: "high",
+      codexReasoningSummaryPreference: "detailed",
+    };
+
+    const input: Record<string, unknown> = {
+      model: "gpt-5-codex",
+      input: [],
+      reasoning: { effort: "low", summary: "auto", extra: "keep" },
+    };
+
+    const output = applyCodexProviderOverrides(provider as any, input);
+    expect(output.reasoning).toEqual({ effort: "high", summary: "detailed", extra: "keep" });
+    expect((input.reasoning as any).effort).toBe("low");
+  });
+
+  it("当请求缺少 reasoning/text 时,强制值应自动补齐对象结构", () => {
+    const provider = {
+      providerType: "codex",
+      codexReasoningEffortPreference: "minimal",
+      codexReasoningSummaryPreference: "auto",
+      codexTextVerbosityPreference: "high",
+    };
+
+    const input: Record<string, unknown> = {
+      model: "gpt-5-codex",
+      input: [],
+    };
+
+    const output = applyCodexProviderOverrides(provider as any, input);
+    expect(output.reasoning).toEqual({ effort: "minimal", summary: "auto" });
+    expect(output.text).toEqual({ verbosity: "high" });
+  });
+});

+ 14 - 0
tests/unit/proxy/codex-request-sanitizer.test.ts

@@ -22,6 +22,20 @@ describe("Codex 请求清洗 - instructions 透传", () => {
     expect(output.parallel_tool_calls).toBe(true);
   });
 
+  it("当客户端显式设置 parallel_tool_calls=false 时应保留(默认不强制覆写)", async () => {
+    const input: Record<string, unknown> = {
+      instructions: "abc",
+      parallel_tool_calls: false,
+    };
+
+    const output = await sanitizeCodexRequest(input, "gpt-5-codex", "auto", 1, {
+      isOfficialClient: false,
+    });
+
+    expect(output.parallel_tool_calls).toBe(false);
+    expect(input.parallel_tool_calls).toBe(false);
+  });
+
   it("auto 策略也不应写入私有重试标记", async () => {
     const originalInstructions = "abc";
     const input: Record<string, unknown> = { instructions: originalInstructions };

+ 10 - 0
tests/unit/proxy/openai-to-codex-request.test.ts

@@ -37,4 +37,14 @@ describe("OpenAI → Codex 转换 - instructions 透传", () => {
     const output = transformOpenAIRequestToCodex("gpt-5-codex", input, true) as any;
     expect(output.instructions).toBeUndefined();
   });
+
+  it("当输入显式设置 parallel_tool_calls=false 时,应透传到 Codex 请求", () => {
+    const input: Record<string, unknown> = {
+      messages: [{ role: "user", content: "你好" }],
+      parallel_tool_calls: false,
+    };
+
+    const output = transformOpenAIRequestToCodex("gpt-5-codex", input, true) as any;
+    expect(output.parallel_tool_calls).toBe(false);
+  });
 });