{% raw %}
# Console API

[English](console-api.md) | [简体中文](console-api.zh-CN.md)

## 范围

Console 端点统一挂载在 `/console/api` 下。

典型流程：

1. `POST /console/api/login`
2. 获取 bearer 会话令牌
3. 后续 Console 请求都携带该令牌

## 端点

### 鉴权

- `POST /console/api/login` — 使用 console 密钥认证，返回 `{ "token": "..." }`
- `POST /console/api/logout` — 注销当前会话
- `POST /console/api/auth/revoke-all-sessions` — 吊销所有活跃会话。适用于管理员轮换 Console 密码后，强制所有已登录用户重新认证。

### 监控（只读）

- `GET /console/api/providers` — 各协议的凭证汇总：`name`（协议显示名）、`total`、`active`、`error`
- `GET /console/api/upstreams` — 列出所有 upstream 的完整配置（含 `api_keys`、`headers`、`user_agent`、`request_payload`、`request_transform_before`、`request_transform_after`、`response_transform`、`script_error_mode`、`count_token`、`enabled`、`status`）。`status` 字段由 auth store 状态计算得出。API 密钥以脱敏格式返回（保留前后 4 位），编辑 upstream 时前端会调用 `GET /upstreams/{name}/keys` 获取完整密钥。支持分页（`page`、`per_page` 查询参数）。
- `GET /console/api/upstreams/names` — 列出所有 upstream 名称（用于下拉选择）
- `GET /console/api/models` — 列出所有 model 的完整配置（含 `upstream`、`upstream_model_id`、`upstream_inference_profile`、`base_url`、`aliases`、`protocol`、`protocol_label`、`request_payload`、`direct_path`、`request_transform_before`、`request_transform_after`、`response_transform`、`script_error_mode`、`count_token`、`enabled`、`hide_name`）。支持分页（`page`、`per_page` 查询参数）。
- `GET /console/api/models/names` — 列出所有模型名称（用于下拉选择）
- `GET /console/api/rate-limit-status` — 限流器快照：`upstream`、`model`、`config`、`rpm_tokens`、`concurrency_permits`、`tpm_tokens`、`ebp_ceiling`、`ebp_cooloff`
- `GET /console/api/stats` — 汇总统计：`total_upstreams`、`active_protocols`、`active_models`、`active_access_keys`
- `GET /console/api/request-log` — 分页请求日志历史（查询参数：`page`、`per_page`）
- `GET /console/api/logs/stream` — 实时请求日志 SSE 流。需要通过 `?token=<session_token>` 查询参数传入有效的会话令牌。
- `GET /console/api/statistics` — 持久化请求统计（SQLite/PostgreSQL），查询参数：`from`（Unix 时间戳）、`to`（Unix 时间戳）、`group_by`（`model` / `access_key` / `access_key_group` / `model_access_key` / `model_access_key_group` / `model_access_key_access_key_group`）。`model` 字段使用 `[upstream_name]::[upstream_model_id]` 格式。
- `GET /console/api/statistics/timeseries` — 时间序列数据（用于图表，SQLite/PostgreSQL），查询参数：`preset`（`12h` / `24h` / `7d`，默认 `12h`）或 `from` / `to`（Unix 时间戳）。返回 `{ data: {...}, from: <timestamp>, to: <timestamp> }`。粒度根据时间范围自动选择（12h → 60s，24h → 300s，7d → 3600s）。
- `GET /console/api/check-status` — License 授权状态（仅商业版构建可用）。返回 `{ "valid": bool, "expired": bool, "message": string, "customer": string|null, "memory_cap_mb": number|null, "restrict_distributed": bool }`。`memory_cap_mb` 为 `null` 表示已授权无上限，非 `null` 表示当前内存上限（MB）。`restrict_distributed` 为 `true` 表示未授权（Redis 和 PostgreSQL 已禁用，仅限单机模式）。OSS 构建此端点返回 404，前端 banner 自动隐藏。

### 管理

- `POST /console/api/scripts/test` — 测试运行 JavaScript，返回执行结果

### Access Key 管理（细粒度 API）

这些端点通过乐观锁和自动热重载修改数据库（SQLite/PostgreSQL）中的对应记录。

- `GET /console/api/access-keys` — 列出所有 access key，返回 `api_key`、`name`、`groups`、`enabled`、`anonymous` 字段。API 密钥以脱敏格式返回（保留前后 4 位，如 `sk-a***xxxx`），避免在列表中暴露完整密钥。
- `GET /console/api/access-keys/{name}/api-key` — 获取指定 access key 的完整 API 密钥（需认证）。前端点击"复制"按钮时调用此端点，获取完整密钥后立即复制到剪贴板并清除内存。匿名 access key（无 `name`）不支持此端点。
- `PUT /console/api/access-keys/{name}/api-key` — 按 `name` 更新指定 access key 的 API 密钥，请求体：`{ "api_key": "..." }`。验证新 API 密钥未被其他 access key 使用。响应中的 `api_key` 为脱敏格式。匿名 access key 也可以更新 API 密钥，但对认证无影响。
- `POST /console/api/access-keys` — 创建 access key，请求体：`{ "api_key": "...", "name": "...", "groups": ["..."] }`。`groups` 为字符串数组（一个 access key 可归属多个组），省略或传 `[]` 表示不归属任何组。响应中的 `api_key` 为脱敏格式。
- `PUT /console/api/access-keys` — 按 `api_key` 更新已有 access key，请求体：`{ "api_key": "...", "name": "...", "groups": ["..."] }`。响应中的 `api_key` 为脱敏格式。
- `DELETE /console/api/access-keys` — 删除 access key，请求体：`{ "name": "..." }`
- `POST /console/api/access-keys/batch` — 原子批量更新多个 access key（单事务 + 单次热重载；任一校验或鉴权失败整体回滚，无部分应用）。请求体：`{ "names": ["..."], "groups_op": { "mode": "set" | "add" | "remove", "groups": ["..."] }, "enabled": true | false }`。`names` 必须非空，且 `groups_op` 与 `enabled` 至少提供一个。`groups_op.mode` 三种语义：`set` 用 `groups` 替换原有组；`add` 在原有组基础上并集去重（保留原顺序后追加新组）；`remove` 从原有组中移除指定组；`enabled` 用于批量启用/禁用。逐行校验与单条编辑（`PUT /console/api/access-keys`）完全同构：组名长度、`name`/`api_key` 唯一与匿名单例等综合规则、组引用存在性，任一不满足返回 422 且整批不写入。**RBAC**：admin 可批量改任意 key 的组与启停；normal_user 仅能批量启停自己创建的 key，请求中含 `groups_op` 整体返回 403，含他人 key 返回 403。响应：`{ "message": "...", "updated": <n> }`。错误码：400 参数缺失或 `names` 为空、403 越权、404 `names` 含不存在的 key、409 乐观锁版本冲突、422 校验失败。
- `POST /console/api/access-key-groups` — 创建 access key group，请求体：`{ "name": "...", "models": [...], "load_balancers": [...] }`。`models` 与 `load_balancers` 是两个独立维度，各自 `"*"` 表示该维全部放行、空数组表示该维全部拒绝。`models` 只接受模型名（或 `"*"`）；`load_balancers` 只接受负载均衡器名（或 `"*"`），别名在写入侧规整为 canonical 名。**硬切**：往 `models` 里填负载均衡器名返回 422（历史混装写法不再接受）。
- `PUT /console/api/access-key-groups` — 按 `name` 更新已有 access key group，请求体同创建。响应回包与列表接口均含 `models` 与 `load_balancers` 两个字段。
- `DELETE /console/api/access-key-groups` — 删除 access key group，请求体：`{ "name": "..." }`

### Upstream 管理（细粒度 API）

这些端点通过乐观锁和自动热重载修改数据库（SQLite/PostgreSQL）中的对应记录。

- `GET /console/api/upstreams/{name}/keys` — 获取指定 upstream 的完整 API 密钥列表（需认证）。前端打开 upstream 编辑弹窗时调用此端点，用完整密钥填充表单（避免用脱敏密钥覆盖原始值）。
- `POST /console/api/upstreams` — 创建 upstream，请求体：`{ name, protocol, base_url, api_keys: [{key, weight}], headers: {}, user_agent, dns_servers: ["ip" | "tls://ip" | "https://ip", ...], request_payload: [{path, value, mode}], request_transform_before, request_transform_after, response_transform, script_error_mode, count_token, enabled, extra_config: {...} }`（`dns_servers` 同时出现在顶层与 `extra_config`，DB 持久化路径为 `extra_config`。协议和 TLS SNI 从第一个 URI 自动推断：`tls://` → TLS，`https://` → HTTPS，纯 IP → UDP。Cloudflare、Google、阿里、Quad9 等知名供应商的 SNI 自动检测）
- `PUT /console/api/upstreams` — 按 `name` 更新已有 upstream，请求体同上。`name` 字段不可修改。
- `DELETE /console/api/upstreams` — 删除 upstream，请求体：`{ name: "..." }`。如果有 model 引用该 upstream，返回 422 错误。

Protocol 可选值：`openai`、`openai_response`、`openai_images`、`openai_embeddings`、`openai_audio`、`openai_rerank`、`anthropic`、`google`、`aws_converse`、`aws_invoke`、`dashscope`、`passthrough`。

Transform 脚本：字符串表示内联脚本，`{ file: "path.js" }` 表示文件引用。

Payload 规则模式：`overwrite`（覆盖）、`remove`（删除）、`add-if-absent`（不存在时添加）、`append`（追加）、`append-if-missing`（缺失时追加）、`remove-matching`（移除匹配项）、`strip-lines`（去除特定行）、`filter-content-types`（过滤内容类型）、`filter-tools`（过滤 tools 字段）。写入值的模式下，规则的 `value` 若为含 `{{...}}` 的字符串则按表达式解析：用 `|` 连接回退链、取首个非空结果，整条链全空则省略本次写入；可用变量为 `header:Name`（大小写不敏感，头值前后空白视为缺失）、`request_id`、`request_access_key_name`、`request_access_key_group`、`system_prompt_hash`（`access_key_hash` 在此上下文为空）。典型用法是以 `{{header:x-claude-code-session-id|system_prompt_hash}}` 注入 `prompt_cache_key` 这类动态缓存键。

只读列表端点（`/upstreams`、`/upstreams/names`）参见上方监控部分。

### Model 管理（细粒度 API）

这些端点通过乐观锁和自动热重载修改数据库（SQLite/PostgreSQL）中的对应记录。

- `POST /console/api/models` — 创建 model，请求体：`{ name, upstream: [upstream_name], upstream_model_id?, upstream_inference_profile?, base_url?, aliases?, protocol?, direct_path?, request_payload?, request_transform_before?, request_transform_after?, response_transform?, script_error_mode?, count_token?, enabled?, hide_name? }`
- `PUT /console/api/models` — 按 `name` 更新已有 model，请求体同上。`name` 字段不可修改。
- `DELETE /console/api/models` — 删除 model，请求体：`{ name: "..." }`。使用宽容解析，即使配置已无效也能删除；保存前会校验结果。

只读列表端点（`/models`、`/models/names`）参见上方监控部分。

### Load Balancer 管理（仅数据库）

这些端点管理存储在数据库（SQLite/PostgreSQL）中的负载均衡器。Load balancer **不**支持基于文件的配置——它们仅限数据库存储。

- `GET /console/api/load-balancers` — 列出所有负载均衡器，每个包含 `name`、`entries`（`{upstream, model, weight}` 数组）、`enabled`、`retry_on_different_node`、`binding_ttl_secs`、`binding_key_template`、`sticky_session_enabled`
- `GET /console/api/load-balancers/names` — 列出所有负载均衡器名称（用于下拉选择）
- `POST /console/api/load-balancers` — 创建负载均衡器，请求体：`{ name, entries: [{upstream, model, weight}], enabled?, retry_on_different_node?, binding_ttl_secs?, binding_key_template?, sticky_session_enabled? }`。`sticky_session_enabled` 默认 `true`；`binding_ttl_secs` 仅在 `sticky_session_enabled=true` 时生效。
- `PUT /console/api/load-balancers` — 按 `name` 更新已有负载均衡器，请求体同上。`name` 字段不可修改。现有静态绑定会被保留。更新 LB 会自动清空所有动态 dynamic binding，使客户端重新均衡。
- `DELETE /console/api/load-balancers` — 删除负载均衡器，请求体：`{ name: "..." }`。静态绑定通过级联删除自动清理，动态会话也会同步清理。
- `GET /console/api/load-balancers/{name}/status` — LB 实时状态：包含 `entries`（各节点 `upstream`、`model`、`config_weight`、`effective_weight`、`status`）、`sticky_sessions`（动态绑定，含 `binding_key`（已脱敏）、`binding_key_hash`（HMAC-SHA256 hex，用作 URL 标识符）、`display_name`、`access_key_group`、`upstream`、`model`、`last_seen`；已自动过滤与静态绑定冲突的条目，保证并集唯一性）、`static_bindings`（静态绑定，同动态绑定字段外加 `created_at`、`updated_at`、`is_static`）、`total_sessions`（去重后的总数）。
- `DELETE /console/api/load-balancers/{name}/dynamic-bindings` — 清空该 LB 的所有动态绑定。返回 `{ deleted: <数量> }`。
- `DELETE /console/api/load-balancers/{name}/dynamic-bindings/{binding_key_hash}` — 按 HMAC-SHA256 哈希删除单个动态绑定。返回 `{ deleted: true }`，若哈希不匹配任何活跃绑定则返回 404。
- `PATCH /console/api/load-balancers/{name}/dynamic-bindings/{binding_key_hash}` — 将动态绑定重新分配到不同的 upstream+model，请求体：`{ upstream: "...", model: "..." }`。使用 LB 的 `binding_ttl_secs` 作为新绑定 TTL（若找不到 LB 则回退到永久/0）。返回 `{ updated: true }`，若绑定已不存在则返回 404。
- `POST /console/api/load-balancers/{name}/dynamic-bindings/{binding_key_hash}/pin` — 将动态绑定转换为持久静态绑定。可在请求体中覆盖 upstream/model（`{ upstream?, model? }`），不覆盖则使用绑定当前值。若 binding_key 已存在静态绑定则返回 409。

#### 静态绑定

静态绑定将特定 binding_key（API key 或 header 值）固定绑定到指定的 upstream+model，完全绕过 WRR 算法。当绑定的上游不可用（被排除）时，请求返回 503（管理员意图：此 key 仅使用该上游）。429 限流同样不会触发静态绑定的自动故障转移——429 直接返回给客户端。binding_key 在数据库中加密存储（AES-256-GCM），使用 HMAC 盲索引保证唯一性约束。

- `GET /console/api/load-balancers/{name}/static-bindings` — 列出 LB 的所有静态绑定。响应中 `binding_key` 已脱敏；`binding_key_hash`（HMAC-SHA256 hex）作为 URL 标识符包含在响应中。
- `POST /console/api/load-balancers/{name}/static-bindings` — 创建静态绑定，请求体：`{ binding_key, upstream, model }`。若 `binding_key` 已存在返回 409，若 `(upstream, model)` 不在 LB entries 中返回 422。创建成功后自动清理同名动态绑定（若清理失败，下次 LB 更新时自愈）。
- `PUT /console/api/load-balancers/{name}/static-bindings/{binding_key_hash}` — 更新静态绑定（修改 upstream/model，不修改 binding_key）。`{binding_key_hash}` 为 binding key 的 HMAC-SHA256 哈希。请求体：`{ upstream, model }`
- `DELETE /console/api/load-balancers/{name}/static-bindings/{binding_key_hash}` — 删除静态绑定并清理 fallback 期间产生的动态绑定。`{binding_key_hash}` 为 binding key 的 HMAC-SHA256 哈希。

### Model Metadata 管理（数据库覆盖层）

这些端点管理模型元数据的数据库覆盖层，用于覆盖嵌入式静态目录（`meta.toml`）中的默认值。

Model Metadata 系统采用三层架构：
1. **静态目录**（`meta.toml`）：编译期嵌入二进制，定义各模型家族的默认元数据（tokenizer、能力标志等）
2. **数据库覆盖**（`config_settings` 表）：通过 Console API 管理，运行时覆盖静态目录的特定字段
3. **内存注册表**（`Arc<ArcSwap<ModelMetadataRegistry>>`）：字段级 overlay 合并，支持热重载

覆盖机制使用字段级 overlay 合并：多个条目按 priority 从低到高合并，高优先级的 `Some` 字段覆盖低优先级，`None` 字段保留低层值。这使得新增模型只需在数据库中添加差异字段，无需重复完整配置。

- `GET /console/api/settings/model-metadata` — 列出所有元数据条目（静态目录 + 数据库覆盖），每条标注 `source`（`"catalog"` 或 `"db"`）
- `GET /console/api/settings/model-metadata/resolve/{model_name}?protocol=openai_chat` — 解析特定模型的有效元数据，展示 overlay 合并结果。可选 `protocol` 查询参数（`openai_chat`、`anthropic`、`dashscope`、`google` 等）用于 protocol fallback 推断
- `PUT /console/api/settings/model-metadata` — 替换所有数据库覆盖条目，请求体为 `ModelMetadataEntry[]` JSON 数组。每条包含 `patterns`（glob 模式列表）、`priority`（整数）、以及可选字段（`tokenizer_family`、`token_correction_factor`、`tiktoken_encoding`、`disable_thinking_by_default`、`supports_*` 能力标志、`max_input_tokens`、`max_output_tokens`、`pipeline_hints` 等）。验证通过后保存到数据库并触发热重载
- `DELETE /console/api/settings/model-metadata` — 清除所有数据库覆盖条目（静态目录条目不可删除）。清除后 registry 回退到仅使用静态目录

**典型用例**：
- 覆盖特定模型的 token correction factor（如 `qwen-vl-max` 使用不同的 factor）
- 为自定义模型添加 tokenizer_family（如代理模型使用 `gpt` 家族）
- 禁用特定模型的 thinking 模式（如 `qwen-plus` 设置 `disable_thinking_by_default: true`）

### MCP 服务器管理（仅数据库）

这些端点管理存储在数据库（SQLite/PostgreSQL）中的 MCP（Model Context Protocol）服务器。MCP 服务器**不**支持基于文件的配置——它们仅限数据库存储。

MCP 服务器使 LLM 客户端能够通过统一的代理端点 `POST /mcp`（Streamable HTTP）或 `GET /mcp/sse`（SSE legacy）发现和调用外部服务（GitHub、Slack、数据库等）的工具。工具名称使用 `{server_name}.{tool_name}` 前缀格式。客户端使用方式参见 [MCP Gateway](mcp.zh-CN.md)。

- `GET /console/api/mcp-servers` — 列出所有 MCP 服务器，每个包含 `name`、`description`、`transport`、`endpoint`、`auth_header`、`extra_headers`、`defer_loading`、`health_check`、`tags`、`priority`、`max_concurrency`、`rate_limit`、`connection_type`、`idle_timeout_secs`、`version`、`sort_order`、`enabled`
- `GET /console/api/mcp-servers/names` — 列出所有 MCP 服务器名称（用于下拉选择）
- `POST /console/api/mcp-servers` — 创建 MCP 服务器，请求体：`{ name, description?, transport, endpoint, auth_header?, extra_headers?, defer_loading?, health_check?, tags?, priority?, max_concurrency?, rate_limit?, connection_type, idle_timeout_secs?, enabled? }`
- `PUT /console/api/mcp-servers` — 按 `name` 更新已有 MCP 服务器，请求体同上。`name` 字段不可修改。
- `DELETE /console/api/mcp-servers` — 删除 MCP 服务器，请求体：`{ name: "..." }`

Transport 可选值：`streamable_http`（标准 HTTP JSON-RPC）、`sse`（Server-Sent Events）、`rest_bridge`（通过 OpenAPI 规范的 REST API）。

Connection type 可选值：`stateful`（每客户端隔离会话，Type A）、`stateless`（共享连接池，Type B）、`rest_bridge`（无状态 REST API，Type C）。

### 访问规则管理（仅数据库）

基于 HTTP 请求头的访问控制规则。规则存储在数据库中，每次热重载时编译为 `AclPolicy`。评估语义详见 [架构 — Header ACL 规则引擎](architecture.zh-CN.md)。

- `GET /console/api/acl-rules` — 列出所有访问规则，返回 `{ "rules": [...] }`
- `POST /console/api/acl-rules` — 创建规则，请求体：`{ name, rule_type?, condition_key, condition_type, condition_value?, action?, enabled?, sort_order?, scope_key?, scope_group? }`
- `PUT /console/api/acl-rules` — 按 `name` 更新规则，请求体同 POST。`name` 字段不可修改。
- `DELETE /console/api/acl-rules` — 删除规则，请求体：`{ name: "..." }`

字段约束：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `name` | string | 是 | 全局唯一，最长 512 字符。创建后不可修改。 |
| `rule_type` | string | 否 | 目前仅 `"header"`（默认）。 |
| `condition_key` | string | 条件必填 | HTTP 请求头名，最长 512 字符。`condition_type` 为 `any` 时留空。 |
| `condition_type` | string | 是 | 取值之一：`exact`、`prefix`、`regex`、`exists`、`absent`、`any`。`any` 表示条件恒真、不检查任何请求头，规则仅靠作用域生效，此时 `condition_key` 与 `condition_value` 须为空。 |
| `condition_value` | string? | 条件必填 | `exact`/`prefix`/`regex` 必填；`exists`/`absent`/`any` 须为空。最长 1024 字节。正则在写入时校验。 |
| `action` | string | 否 | `"allow"` 或 `"deny"`（默认 `"allow"`）。 |
| `enabled` | bool | 否 | 默认 `true`。禁用后规则在编译时跳过。 |
| `sort_order` | int | 否 | 默认 `0`。数值越小越先评估。相同 `sort_order` 时按作用域 全局 → 密钥组 → 访问密钥 顺序评估，故密钥级规则需更小数值才能覆盖同 order 的全局规则。 |
| `scope_key` | string[] | 否 | 限定到一个或多个 access key。与 `scope_group` 互斥。空数组=全局。 |
| `scope_group` | string[] | 否 | 限定到一个或多个密钥组。与 `scope_key` 互斥。空数组=全局。 |

**匹配语义**：评估时合并当前请求作用域内的规则（全局 + 当前 key 的 per-key + 当前 group 的 per-group），按 `sort_order` 升序逐条匹配，**首条命中即决定 allow/deny 并短路**（`deny` 立即返回 403，`allow` 放行到下一层），不再评估后续规则；无规则命中时回退 default_action 链（per-key → per-group → 全局 `Allow`）。

## 访问控制

- Console API 端点（`/console/api/*`）始终挂载
- API 访问需要有效的会话令牌（来自 `/login`）或配置 `secret_key` 作为 Bearer 令牌
- Web UI（`/console`）仅在 `web_console.enabled` 开启时提供服务
- 默认允许所有 IP 访问。设置 `web_console.allow_remote = false` 可限制为仅限本机。
- 防爆破保护：IP 连续失败 `web_console.max_failures` 次（默认 5 次）登录后被自动封禁 `web_console.ban_duration` 秒（默认 300 秒/5 分钟）。失败计数器采用滑动窗口——距离上次失败超过封禁时长则重置。没有手动封禁/解封 API — 那属于 WAF 的职责。
- IP 封禁可通过 `web_console.ip_ban_enabled` 关闭（默认 `true`）。适用于多个用户共享同一 IP 的场景（VPN、NAT、反向代理）。关闭后防爆破退化为 bcrypt 计算成本（~200-400ms/次）+ 已验证 token 缓存保护。
- Console 响应包含安全头：`X-Content-Type-Options: nosniff`、`X-Frame-Options: DENY`、`Referrer-Policy`、`Content-Security-Policy`，防止 XSS、点击劫持等攻击。

## Console

Web 控制台位于 `/console`，使用 React 构建（通过 Vite 编译并使用 rust-embed 嵌入二进制）。

侧边栏：
- **Overview** — 汇总统计卡片（Upstreams、Protocols、Models、Access Keys），以及时间序列图表（input tokens 和请求数随时间变化），支持 12h/24h/7d 预设切换
- **Access Keys** — 管理 API access key 和 access key group（Access Keys 标签页：创建/编辑/删除 access key，配置 API 密钥、名称、所属组；Groups 标签页：创建/编辑/删除 access key group，配置模型白名单）。Access key group 支持 `*` 表示允许访问所有模型
- **Statistics** — 持久化请求用量统计（SQLite/PostgreSQL），支持时间范围选择（24h/7d/30d/自定义）、分组切换（By Model / By Access Key / By Group / By Model + Access Key / By Model + Group）、Summary 卡片与数据表格。Model 列显示 `upstream_name::upstream_model_id` 格式。
- **Upstreams** — 完整的 upstream 管理，支持 CRUD：新增/编辑/删除 upstream（含 API 密钥、请求头、Payload 规则、Transform 脚本及所有高级配置，通过模态框表单编辑）。Model 内嵌在各 upstream 区域中展示，支持独立的 CRUD（新增/编辑/删除 model，支持 upstream 选择、model ID 覆盖、别名、协议覆盖、Payload 规则和 Transform）。
- **Load Balancers** — 管理仅限数据库的负载均衡器，支持多提供商路由、加权流量分配和 dynamic binding。支持 LB entries（upstream+model+weight）的 CRUD、静态绑定（将特定 API key 固定到指定 upstream）以及运行时会话管理。状态页展示各节点状态（effective weight、status）和所有活跃 dynamic binding（动态+静态），可直接删除、重新分配或固定会话。
- **MCP Servers** — 管理仅限数据库的 MCP（Model Context Protocol）服务器，使 LLM 客户端能够调用外部服务（GitHub、Slack、数据库等）的工具。支持 MCP 服务器配置的 CRUD，包括传输类型（Streamable HTTP、SSE、REST Bridge）、连接类型（stateful、stateless、REST bridge）、认证、标签、优先级和限流。每个服务器的工具通过 `POST /mcp`（Streamable HTTP）和 `GET /mcp/sse`（SSE）暴露给客户端。工具名称使用 `{server_name}.{tool_name}` 前缀格式。
- **Logs** — 分页请求日志与实时 SSE 流。Stream 标签页显示实时请求/响应元数据（model、upstream、status、latency、tokens）。日志条目展示完整的请求/响应头和正文片段（截断于 1MB）。支持关键词过滤，标签页失去可见性时自动暂停以节省资源。

无效或过期的 token 会被自动清除并跳转回登录页。

## 说明

- 凭证由配置文件驱动，Console API 中为只读
- **标准 section 顺序**：通过细粒度 API 端点（如 access key 管理）保存配置时，顶层 section 会按标准顺序重排：`server` → `web_console` → `cors` → `streaming` → `logging` → `rate_limit_per_user` → `retry` → `routing` → `access_key_groups` → `access_keys` → `upstreams` → `models`。Load balancers 仅限数据库存储，不出现在基于文件的配置中——它们不属于标准文件顺序。这一设计兼顾人类可读性与程序化一致性——手动编辑文件的运维人员看到可预期的结构，程序始终产出稳定输出而无论输入文件的 section 顺序如何。每个 section 内部的注释、格式、字段顺序完全保留。未知 section 会被拒绝。未来新增配置字段必须在此顺序中指定位置。
- 请求日志是运维元数据，不等同于完整审计系统
- 统计功能基于 SQLite/PostgreSQL 持久化存储，1 分钟粒度，数据可能因 flush 间隔有最多 1 分钟的延迟
- API 响应中的 `protocol` 字段使用显示名称（如 `OpenAI Chat Completions`、`Anthropic`、`Gemini`）
{% endraw %}
