Improve gateway transport and usage runtime

This commit is contained in:
elky
2026-06-25 22:36:27 +08:00
parent d336d1a7fa
commit 6f00e9fc67
112 changed files with 12456 additions and 1387 deletions
@@ -15,6 +15,7 @@ use base64::Engine as _;
use futures_util::stream::{self, BoxStream};
use futures_util::StreamExt;
use http::{HeaderMap, HeaderName, HeaderValue};
use http_body_util::BodyExt;
use regex::{Captures, Regex};
use serde_json::{json, Map, Value};
use uuid::Uuid;
@@ -29,10 +30,10 @@ use crate::clock::current_unix_secs;
use crate::execution_runtime::ndjson::encode_stream_frame_ndjson;
use crate::execution_runtime::transport::{
build_browser_wreq_client, build_request_body, build_request_headers,
decode_response_body_bytes, format_upstream_request_error, format_wreq_upstream_request_error,
resolve_stream_first_byte_timeout, send_request, stream_first_byte_timeout_message,
with_non_stream_total_timeout, DirectHttpResponse, ExecutionRuntimeTransportError,
ExecutionTransportControls,
decode_response_body_bytes, format_hyper_error_chain, format_upstream_request_error,
format_wreq_upstream_request_error, resolve_stream_first_byte_timeout, send_request,
stream_first_byte_timeout_message, with_non_stream_total_timeout, DirectHttpResponse,
ExecutionRuntimeTransportError, ExecutionTransportControls,
};
const GROK_INTERNAL_HEADER: &str = "x-aether-grok-runtime";
@@ -503,6 +504,15 @@ async fn collect_grok_response_stream(
collect_grok_response_chunk(status_code, upstream_bytes, raw_body, adapter, &chunk);
}
}
DirectHttpResponse::HyperH2c(response) => {
let mut stream = response.into_body().into_data_stream();
while let Some(chunk) = stream.next().await {
let chunk = chunk.map_err(|err| {
ExecutionRuntimeTransportError::UpstreamRequest(format_hyper_error_chain(&err))
})?;
collect_grok_response_chunk(status_code, upstream_bytes, raw_body, adapter, &chunk);
}
}
DirectHttpResponse::BrowserWreq(response) => {
let mut stream = response.bytes_stream();
while let Some(chunk) = stream.next().await {
@@ -547,6 +557,16 @@ fn grok_response_body_stream(response: DirectHttpResponse) -> GrokUpstreamBodySt
})
})
.boxed(),
DirectHttpResponse::HyperH2c(response) => response
.into_body()
.into_data_stream()
.map(|chunk| {
chunk.map_err(|err| {
ExecutionRuntimeTransportError::UpstreamRequest(format_hyper_error_chain(&err))
.to_string()
})
})
.boxed(),
DirectHttpResponse::BrowserWreq(response) => response
.bytes_stream()
.map(|chunk| {
@@ -48,6 +48,15 @@ pub use server::{
build_execution_runtime_router_with_request_gates, serve_execution_runtime_tcp,
serve_execution_runtime_unix,
};
pub use transport::DirectH2cSenderPrewarmReport;
pub async fn prewarm_direct_h2c_sender_cache_from_env_for_startup(
) -> Result<Option<DirectH2cSenderPrewarmReport>, String> {
transport::prewarm_direct_h2c_sender_cache_from_env()
.await
.map_err(|err| err.to_string())
}
pub(crate) use stream::execute_execution_runtime_stream;
pub(crate) use stream_pump::build_direct_execution_frame_stream;
pub(crate) use sync::{
File diff suppressed because it is too large Load Diff
@@ -11,6 +11,7 @@ use async_stream::stream;
use axum::body::Bytes;
use base64::Engine as _;
use futures_util::{Stream, StreamExt};
use http_body_util::BodyExt;
use serde_json::Value;
use tracing::warn;
@@ -20,7 +21,8 @@ use crate::ai_serving::api::{
};
use crate::execution_runtime::ndjson::encode_stream_frame_ndjson;
use crate::execution_runtime::transport::{
format_wreq_upstream_request_error, stream_first_byte_timeout_message, DirectUpstreamResponse,
format_hyper_error_chain, format_wreq_upstream_request_error,
stream_first_byte_timeout_message, DirectUpstreamResponse,
};
use crate::execution_runtime::DirectUpstreamStreamExecution;
use crate::GatewayError;
@@ -273,6 +275,88 @@ pub(crate) fn build_direct_execution_frame_stream(
}
}
}
DirectUpstreamResponse::HyperH2c(response) => {
let mut bytes_stream = response.into_body().into_data_stream();
loop {
let item = if ttfb_ms.is_none() {
match await_stream_first_byte(
bytes_stream.next(),
started_at,
stream_first_byte_timeout,
)
.await
{
Ok(item) => item,
Err(timeout) => {
match encode_first_byte_timeout_frame(timeout) {
Ok(frame) => yield Ok(frame),
Err(err) => {
yield Err(err);
return;
}
}
break;
}
}
} else {
bytes_stream.next().await
};
let Some(item) = item else {
break;
};
match item {
Ok(chunk) => {
if ttfb_ms.is_none() {
ttfb_ms = Some(started_at.elapsed().as_millis() as u64);
}
if !first_chunk_telemetry_emitted {
match encode_telemetry_frame(ttfb_ms, ttfb_ms, upstream_bytes) {
Ok(frame) => yield Ok(frame),
Err(err) => {
yield Err(err);
return;
}
}
first_chunk_telemetry_emitted = true;
}
upstream_bytes += chunk.len() as u64;
observe_stream_chunk(
&mut stream_terminal_observer,
&normalized_observer_context,
private_stream_normalizer.as_mut(),
&mut observer_buffered,
chunk.as_ref(),
);
match encode_data_frame(&chunk) {
Ok(frame) => yield Ok(frame),
Err(err) => {
yield Err(err);
return;
}
}
}
Err(err) => {
let message = format_hyper_error_chain(&err);
warn!(
event_name = "stream_pump_body_read_error",
log_type = "ops",
status_code,
upstream_bytes,
error = %message,
"upstream body stream read error"
);
match encode_error_frame(status_code, message) {
Ok(frame) => yield Ok(frame),
Err(encode_err) => {
yield Err(encode_err);
return;
}
}
break;
}
}
}
}
DirectUpstreamResponse::BrowserWreq(response) => {
let mut bytes_stream = response.bytes_stream();
loop {
@@ -662,6 +746,60 @@ async fn buffer_non_sse_upstream_body(
}
}
}
DirectUpstreamResponse::HyperH2c(response) => {
let mut bytes_stream = response.into_body().into_data_stream();
loop {
let item = if ttfb_ms.is_none() {
match await_stream_first_byte(
bytes_stream.next(),
started_at,
stream_first_byte_timeout,
)
.await
{
Ok(item) => item,
Err(timeout) => {
return Err(BufferedUpstreamBodyError {
message: stream_first_byte_timeout_message(timeout),
ttfb_ms,
upstream_bytes,
first_byte_timeout: Some(timeout),
});
}
}
} else {
bytes_stream.next().await
};
let Some(item) = item else {
break;
};
match item {
Ok(chunk) => {
if ttfb_ms.is_none() {
ttfb_ms = Some(started_at.elapsed().as_millis() as u64);
}
upstream_bytes += chunk.len() as u64;
body_bytes.extend_from_slice(&chunk);
}
Err(err) => {
let message = format_hyper_error_chain(&err);
warn!(
event_name = "stream_pump_body_read_error",
log_type = "ops",
upstream_bytes,
error = %message,
"upstream body stream read error"
);
return Err(BufferedUpstreamBodyError {
message,
ttfb_ms,
upstream_bytes,
first_byte_timeout: None,
});
}
}
}
}
DirectUpstreamResponse::BrowserWreq(response) => {
let mut bytes_stream = response.bytes_stream();
loop {
@@ -22,6 +22,7 @@ use axum::body::{to_bytes, Body, Bytes};
use axum::http::header::{CACHE_CONTROL, CONTENT_ENCODING, CONTENT_LENGTH, CONTENT_TYPE};
use axum::http::{HeaderName, HeaderValue, Response, StatusCode};
use futures_util::StreamExt;
use http_body_util::BodyExt;
use serde_json::{json, Value};
use tokio::sync::mpsc;
use tokio::sync::Mutex;
@@ -55,9 +56,9 @@ use crate::execution_runtime::submission::{
};
use crate::execution_runtime::transport::{
build_execution_response_body, build_request_body, collect_response_headers,
decode_response_body_bytes, format_upstream_request_error, format_wreq_upstream_request_error,
response_body_is_json, send_request, DirectHttpResponse, DirectSyncExecutionRuntime,
ExecutionRuntimeTransportError,
decode_response_body_bytes, format_hyper_error_chain, format_upstream_request_error,
format_wreq_upstream_request_error, response_body_is_json, send_request, DirectHttpResponse,
DirectSyncExecutionRuntime, ExecutionRuntimeTransportError,
};
use crate::execution_runtime::windsurf::maybe_execute_windsurf_sync;
use crate::execution_runtime::{
@@ -1165,6 +1166,23 @@ async fn execute_openai_image_sync_upstream_sse_candidate(
body_bytes.extend_from_slice(&chunk);
}
}
DirectHttpResponse::HyperH2c(response) => {
let mut upstream_stream = response.into_body().into_data_stream();
while let Some(chunk) = upstream_stream.next().await {
let chunk = chunk.map_err(|err| {
SyncExecutionFailure::from_transport(
ExecutionRuntimeTransportError::UpstreamRequest(format_hyper_error_chain(
&err,
)),
)
})?;
let elapsed_ms = started_at.elapsed().as_millis() as u64;
progress
.observe_chunk(&chunk, status_code, elapsed_ms)
.await;
body_bytes.extend_from_slice(&chunk);
}
}
DirectHttpResponse::BrowserWreq(response) => {
let mut upstream_stream = response.bytes_stream();
while let Some(chunk) = upstream_stream.next().await {
@@ -1522,7 +1540,8 @@ async fn execute_execution_runtime_sync_impl(
let lifecycle_seed = build_lifecycle_usage_seed(&plan, report_context.as_ref());
state
.usage_runtime
.record_pending(state.data.as_ref(), lifecycle_seed);
.record_pending_direct(state.data.as_ref(), lifecycle_seed)
.await;
record_local_request_candidate_status(
state,
&plan,
File diff suppressed because it is too large Load Diff