mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-07 01:47:47 +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:
@@ -20,9 +20,11 @@ use crate::ai_serving::LocalExecutionAttemptSource;
|
||||
use crate::clock::current_unix_ms;
|
||||
use crate::control::GatewayControlDecision;
|
||||
use crate::execution_runtime::{
|
||||
build_transport_error_stop_response, execute_execution_runtime_stream_with_retry_scope,
|
||||
acquire_upstream_execution_gate, build_transport_error_stop_response,
|
||||
execute_execution_runtime_stream_with_retry_scope,
|
||||
execute_execution_runtime_sync_with_retry_scope,
|
||||
mark_stream_candidate_watchdog_terminal_started, StreamCandidateWatchdogProgress,
|
||||
UpstreamExecutionGateProvider, UPSTREAM_EXECUTION_GATE_NAME,
|
||||
};
|
||||
use crate::executor::{
|
||||
build_local_execution_exhaustion, mark_deferred_upstream_response, LocalExecutionRequestOutcome,
|
||||
@@ -43,7 +45,6 @@ use crate::stage_metrics::observe_gateway_stage_ms;
|
||||
use crate::{AppState, GatewayError};
|
||||
|
||||
const DEFAULT_STREAM_FIRST_BYTE_WATCHDOG_TIMEOUT_MS: u64 = 30_000;
|
||||
const UPSTREAM_EXECUTION_GATE_NAME: &str = "gateway_upstream_execution";
|
||||
const UPSTREAM_TARGET_GATE_NAME: &str = "gateway_upstream_target";
|
||||
const UPSTREAM_EXECUTION_GATE_HOLD_STREAM_RESPONSE_ENV: &str =
|
||||
"AETHER_GATEWAY_UPSTREAM_EXECUTION_GATE_HOLD_STREAM_RESPONSE";
|
||||
@@ -1612,47 +1613,6 @@ fn hold_response_upstream_execution_permit(
|
||||
Response::from_parts(parts, Body::from_stream(stream))
|
||||
}
|
||||
|
||||
trait UpstreamExecutionGateProvider {
|
||||
fn upstream_execution_gate(&self) -> Option<&aether_runtime::ConcurrencyGate>;
|
||||
fn upstream_execution_gate_queue_budget(&self) -> Duration;
|
||||
}
|
||||
|
||||
impl UpstreamExecutionGateProvider for AppState {
|
||||
fn upstream_execution_gate(&self) -> Option<&aether_runtime::ConcurrencyGate> {
|
||||
self.upstream_execution_gate.as_deref()
|
||||
}
|
||||
|
||||
fn upstream_execution_gate_queue_budget(&self) -> Duration {
|
||||
self.frontdoor_runtime_guards.internal_gate_queue_budget
|
||||
}
|
||||
}
|
||||
|
||||
async fn acquire_upstream_execution_gate(
|
||||
state: &(impl UpstreamExecutionGateProvider + ?Sized),
|
||||
trace_id: &str,
|
||||
) -> Result<Option<ConcurrencyPermit>, GatewayError> {
|
||||
let Some(gate) = state.upstream_execution_gate() else {
|
||||
return Ok(None);
|
||||
};
|
||||
let budget = state.upstream_execution_gate_queue_budget();
|
||||
let gate_wait_started_at = std::time::Instant::now();
|
||||
match timeout(budget, gate.acquire()).await {
|
||||
Ok(Ok(permit)) => {
|
||||
observe_gateway_stage_ms(
|
||||
"upstream_execution_gate_wait",
|
||||
gate_wait_started_at.elapsed().as_millis() as u64,
|
||||
);
|
||||
Ok(Some(permit))
|
||||
}
|
||||
Ok(Err(err)) => Err(GatewayError::Internal(err.to_string())),
|
||||
Err(_) => Err(GatewayError::AdmissionTimeout {
|
||||
trace_id: trace_id.to_string(),
|
||||
gate: UPSTREAM_EXECUTION_GATE_NAME,
|
||||
queue_budget_ms: budget.as_millis() as u64,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn mark_unused_local_candidate_items<T, FPlan, FContext>(
|
||||
state: &AppState,
|
||||
remaining: Vec<T>,
|
||||
|
||||
Reference in New Issue
Block a user