mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-11 11:49:50 +08:00
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:
@@ -0,0 +1,61 @@
|
||||
//! OpenAI Responses WebSocket protocol entry point, session engine, and adapters.
|
||||
//!
|
||||
//! The route is protocol-oriented. `session` bootstraps the authenticated
|
||||
//! connection, `connection` owns the socket FSM, `client` and `quota` own
|
||||
//! protocol/retry policy, and `lifecycle`/`turn` bridge each turn into the
|
||||
//! existing usage and audit runtime. Adapters contain only provider-specific
|
||||
//! connection and metadata behavior.
|
||||
|
||||
mod adapter;
|
||||
mod adapters;
|
||||
mod admission;
|
||||
mod binding;
|
||||
mod client;
|
||||
mod connection;
|
||||
mod frame;
|
||||
mod lifecycle;
|
||||
mod quota;
|
||||
mod relay_policy;
|
||||
mod request;
|
||||
mod session;
|
||||
mod state;
|
||||
mod turn;
|
||||
mod upstream;
|
||||
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use axum::body::Body;
|
||||
use axum::extract::ws::WebSocketUpgrade;
|
||||
use axum::extract::{ConnectInfo, State};
|
||||
use axum::http::{HeaderMap, Response, Uri};
|
||||
|
||||
use crate::handlers::proxy::websocket::ingress::{
|
||||
upgrade_authenticated_ai_websocket, WebSocketIngressSpec,
|
||||
};
|
||||
use crate::handlers::proxy::websocket::session::RESPONSES_WEBSOCKET_SESSION_LIMITS;
|
||||
use crate::{AppState, GatewayError};
|
||||
|
||||
pub(crate) async fn responses_websocket(
|
||||
State(state): State<AppState>,
|
||||
ConnectInfo(remote_addr): ConnectInfo<SocketAddr>,
|
||||
ws: WebSocketUpgrade,
|
||||
headers: HeaderMap,
|
||||
uri: Uri,
|
||||
) -> Result<Response<Body>, GatewayError> {
|
||||
upgrade_authenticated_ai_websocket(
|
||||
state,
|
||||
remote_addr,
|
||||
ws,
|
||||
headers,
|
||||
uri,
|
||||
RESPONSES_WEBSOCKET_SESSION_LIMITS,
|
||||
RESPONSES_WEBSOCKET_INGRESS_SPEC,
|
||||
session::run_responses_websocket,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
const RESPONSES_WEBSOCKET_INGRESS_SPEC: WebSocketIngressSpec = WebSocketIngressSpec {
|
||||
route_unavailable_message: "WebSocket route is unavailable",
|
||||
ip_whitelist_failure_event_name: "responses_websocket_ip_whitelist_check_failed",
|
||||
};
|
||||
Reference in New Issue
Block a user