feat(gateway): Codex/OpenAI Responses WebSocket 代理模式

在 /v1/responses 上支持 WebSocket 升级,把客户端帧中继到上游 Codex /
OpenAI Responses WebSocket 端点,同时保持既有的路由、鉴权、配额与用量
语义:

- 路由与准入:control/route/ai.rs 识别 WebSocket 升级请求;
  websocket/ingress.rs 复用 API Key 鉴权、IP 规则与并发许可,并引入
  独立的 WebSocket 连接许可
- 中继:websocket/responses/* 按 connection / session / turn 分层,
  帧解析归一化、socket 写入有界、continuation 保持调度亲和性
- 配额:orchestration/codex_quota_breaker.rs 在账号配额耗尽时熔断并
  自动恢复,不再直接断开客户端连接
- 用量:每个 turn 的终态用量落库,request_metadata 记录
  websocket_mode / websocket_transport,管理端与 usage 视图暴露
  is_websocket
- 管理端:provider 可配置 Responses WebSocket 开关
This commit is contained in:
AAEE86
2026-08-17 14:50:33 +08:00
committed by ZheFox
parent 9a0d346ff3
commit 71b54070e8
72 changed files with 10441 additions and 108 deletions
@@ -185,6 +185,7 @@ SELECT
OR NULLIF(BTRIM("usage".request_metadata->>'provider_actual_service_tier'), '') IS NOT NULL
OR ("usage".request_metadata->>'client_requested_stream') IN ('true', 'false')
OR ("usage".request_metadata->>'upstream_is_stream') IN ('true', 'false')
OR ("usage".request_metadata->>'websocket_mode') IN ('true', 'false')
THEN jsonb_strip_nulls(jsonb_build_object(
'client_ip',
NULLIF(BTRIM("usage".request_metadata->>'client_ip'), ''),
@@ -213,6 +214,12 @@ SELECT
WHEN ("usage".request_metadata->>'upstream_is_stream') IN ('true', 'false')
THEN ("usage".request_metadata->>'upstream_is_stream')::boolean
ELSE NULL
END,
'websocket_mode',
CASE
WHEN ("usage".request_metadata->>'websocket_mode') IN ('true', 'false')
THEN ("usage".request_metadata->>'websocket_mode')::boolean
ELSE NULL
END
))::json
ELSE NULL::json
@@ -185,6 +185,7 @@ SELECT
OR NULLIF(BTRIM("usage".request_metadata->>'provider_actual_service_tier'), '') IS NOT NULL
OR ("usage".request_metadata->>'client_requested_stream') IN ('true', 'false')
OR ("usage".request_metadata->>'upstream_is_stream') IN ('true', 'false')
OR ("usage".request_metadata->>'websocket_mode') IN ('true', 'false')
THEN jsonb_strip_nulls(jsonb_build_object(
'client_ip',
NULLIF(BTRIM("usage".request_metadata->>'client_ip'), ''),
@@ -213,6 +214,12 @@ SELECT
WHEN ("usage".request_metadata->>'upstream_is_stream') IN ('true', 'false')
THEN ("usage".request_metadata->>'upstream_is_stream')::boolean
ELSE NULL
END,
'websocket_mode',
CASE
WHEN ("usage".request_metadata->>'websocket_mode') IN ('true', 'false')
THEN ("usage".request_metadata->>'websocket_mode')::boolean
ELSE NULL
END
))::json
ELSE NULL::json
@@ -3221,6 +3221,8 @@ fn usage_sql_uses_json_null_placeholders_for_usage_payload_columns() {
assert!(sql.contains("request_metadata->>'provider_reasoning_effort'"));
assert!(sql.contains("request_metadata->>'provider_service_tier'"));
assert!(sql.contains("request_metadata->>'provider_actual_service_tier'"));
assert!(sql.contains("request_metadata->>'websocket_mode'"));
assert!(sql.contains("'websocket_mode'"));
assert!(sql.contains("AS client_family"));
assert!(sql.contains("request_metadata->'client_session_affinity'->>'client_family'"));
assert!(sql.contains("request_metadata->>'client_family'"));