refactor: 大规模模块拆分与代码精简,新增 ai-pipeline/data-contracts 独立 crate

- 新增 aether-ai-pipeline 和 aether-data-contracts crate,将 pipeline 逻辑与数据契约从 gateway 中解耦
- 重构 admin handlers:拆分单体模块为 auth/billing/endpoint/features/model/observability/provider/system 等独立子模块
- 合并 chat/cli 重复代码路径:精简 conversion、finalize、planner 中的 sync/chat/cli 分支
- 重构 scheduler/executor/data 层,引入 facade 模式降低模块间耦合
- 移除冗余的 intent 模块,将 plan_fallback/policy/stream_path/sync_path 迁移至 executor
- 前端适配:调整 admin API 调用和 provider 模型测试对话框
This commit is contained in:
fawney19
2026-04-07 02:50:19 +08:00
parent 763ff03a7b
commit 5d96d6673b
732 changed files with 28593 additions and 20666 deletions

View File

@@ -1,14 +1,14 @@
use axum::body::Body;
use axum::http::Response;
use aether_data_contracts::repository::candidates::RequestCandidateStatus;
use crate::ai_pipeline::planner::plan_builders::{
LocalStreamPlanAndReport, LocalSyncPlanAndReport,
};
use crate::control::GatewayControlDecision;
use crate::execution_runtime::{
execute_execution_runtime_stream, execute_execution_runtime_sync,
};
use crate::scheduler::record_local_request_candidate_status;
use crate::execution_runtime::{execute_execution_runtime_stream, execute_execution_runtime_sync};
use crate::request_candidate_runtime::record_local_request_candidate_status;
use crate::{AppState, GatewayError};
pub(crate) trait LocalPlanAndReport {
@@ -120,7 +120,33 @@ where
state,
plan_and_report.plan(),
plan_and_report.report_context().as_ref(),
aether_data::repository::candidates::RequestCandidateStatus::Unused,
RequestCandidateStatus::Unused,
None,
None,
None,
None,
None,
None,
)
.await;
}
}
pub(crate) async fn mark_unused_local_candidate_items<T, FPlan, FContext>(
state: &AppState,
remaining: Vec<T>,
plan: FPlan,
report_context: FContext,
) where
FPlan: Fn(&T) -> &aether_contracts::ExecutionPlan,
FContext: Fn(&T) -> Option<&serde_json::Value>,
{
for item in remaining {
record_local_request_candidate_status(
state,
plan(&item),
report_context(&item),
RequestCandidateStatus::Unused,
None,
None,
None,

View File

@@ -1,7 +1,29 @@
pub(crate) mod candidate_loop;
mod diagnostics;
mod orchestration;
mod plan_fallback;
mod policy;
mod remote;
mod reports;
mod retries;
mod stream_path;
mod sync_path;
pub(crate) use crate::request_candidate_runtime::{
persist_available_local_candidate, persist_skipped_local_candidate,
};
pub(crate) use candidate_loop::mark_unused_local_candidate_items;
pub(crate) use orchestration::*;
pub(crate) use plan_fallback::{
maybe_execute_stream_via_plan_fallback, maybe_execute_sync_via_plan_fallback,
};
pub(crate) use policy::{
build_direct_plan_bypass_cache_key, mark_direct_plan_bypass,
should_bypass_execution_runtime_decision, should_bypass_execution_runtime_plan,
should_skip_direct_plan,
};
pub(crate) use remote::{
maybe_execute_stream_via_remote_decision, maybe_execute_sync_via_remote_decision,
};
pub(crate) use stream_path::maybe_execute_via_stream_decision_path;
pub(crate) use sync_path::maybe_execute_via_sync_decision_path;

View File

@@ -1,6 +1,7 @@
use axum::body::Body;
use axum::http::Response;
use crate::ai_pipeline::planner;
use crate::ai_pipeline::planner::common::{
parse_direct_request_body, EXECUTION_RUNTIME_STREAM_DECISION_ACTION,
EXECUTION_RUNTIME_SYNC_DECISION_ACTION,
@@ -16,12 +17,10 @@ use crate::ai_pipeline::planner::standard::family::{
build_local_sync_plan_and_reports as build_standard_sync_plan_and_reports, LocalStandardSpec,
};
use crate::ai_pipeline::planner::standard::{claude, gemini, openai};
use crate::ai_pipeline::{planner, runtime};
use crate::control::GatewayControlDecision;
use crate::executor::candidate_loop::{
execute_stream_plan_and_reports, execute_sync_plan_and_reports,
};
use crate::intent;
use crate::{AppState, GatewayControlSyncDecisionResponse, GatewayError};
pub(crate) async fn maybe_execute_sync_local_path(
@@ -31,7 +30,7 @@ pub(crate) async fn maybe_execute_sync_local_path(
trace_id: &str,
decision: &GatewayControlDecision,
) -> Result<Option<Response<Body>>, GatewayError> {
intent::maybe_execute_via_sync_intent_path(state, parts, body_bytes, trace_id, decision).await
super::maybe_execute_via_sync_decision_path(state, parts, body_bytes, trace_id, decision).await
}
pub(crate) async fn maybe_execute_stream_local_path(
@@ -41,7 +40,8 @@ pub(crate) async fn maybe_execute_stream_local_path(
trace_id: &str,
decision: &GatewayControlDecision,
) -> Result<Option<Response<Body>>, GatewayError> {
intent::maybe_execute_via_stream_intent_path(state, parts, body_bytes, trace_id, decision).await
super::maybe_execute_via_stream_decision_path(state, parts, body_bytes, trace_id, decision)
.await
}
pub(crate) async fn maybe_execute_sync_via_local_decision(
@@ -225,10 +225,7 @@ pub(crate) async fn maybe_execute_sync_via_local_standard_decision(
decision,
body_json,
plan_kind,
|plan_kind| {
claude::chat::resolve_sync_spec(plan_kind)
.or_else(|| claude::cli::resolve_sync_spec(plan_kind))
},
claude::resolve_sync_spec,
)
.await?
{
@@ -242,10 +239,7 @@ pub(crate) async fn maybe_execute_sync_via_local_standard_decision(
decision,
body_json,
plan_kind,
|plan_kind| {
gemini::chat::resolve_sync_spec(plan_kind)
.or_else(|| gemini::cli::resolve_sync_spec(plan_kind))
},
gemini::resolve_sync_spec,
)
.await
}
@@ -265,10 +259,7 @@ pub(crate) async fn maybe_execute_stream_via_local_standard_decision(
decision,
body_json,
plan_kind,
|plan_kind| {
claude::chat::resolve_stream_spec(plan_kind)
.or_else(|| claude::cli::resolve_stream_spec(plan_kind))
},
claude::resolve_stream_spec,
)
.await?
{
@@ -282,10 +273,7 @@ pub(crate) async fn maybe_execute_stream_via_local_standard_decision(
decision,
body_json,
plan_kind,
|plan_kind| {
gemini::chat::resolve_stream_spec(plan_kind)
.or_else(|| gemini::cli::resolve_stream_spec(plan_kind))
},
gemini::resolve_stream_spec,
)
.await
}
@@ -434,7 +422,28 @@ pub(crate) async fn maybe_execute_sync_request(
trace_id: &str,
decision: Option<&GatewayControlDecision>,
) -> Result<Option<Response<Body>>, GatewayError> {
runtime::maybe_execute_sync_request(state, parts, body_bytes, trace_id, decision).await
let Some(decision) = decision else {
return Ok(None);
};
#[cfg(not(test))]
{
if parts.method != http::Method::POST {
return Ok(None);
}
return maybe_execute_sync_local_path(state, parts, body_bytes, trace_id, decision).await;
}
#[cfg(test)]
{
if state
.execution_runtime_override_base_url()
.unwrap_or_default()
.is_empty()
&& parts.method != http::Method::POST
{
return Ok(None);
}
maybe_execute_sync_local_path(state, parts, body_bytes, trace_id, decision).await
}
}
pub(crate) async fn maybe_execute_stream_request(
@@ -444,7 +453,28 @@ pub(crate) async fn maybe_execute_stream_request(
trace_id: &str,
decision: Option<&GatewayControlDecision>,
) -> Result<Option<Response<Body>>, GatewayError> {
runtime::maybe_execute_stream_request(state, parts, body_bytes, trace_id, decision).await
let Some(decision) = decision else {
return Ok(None);
};
#[cfg(not(test))]
{
if parts.method != http::Method::POST {
return Ok(None);
}
return maybe_execute_stream_local_path(state, parts, body_bytes, trace_id, decision).await;
}
#[cfg(test)]
{
if state
.execution_runtime_override_base_url()
.unwrap_or_default()
.is_empty()
&& parts.method != http::Method::POST
{
return Ok(None);
}
maybe_execute_stream_local_path(state, parts, body_bytes, trace_id, decision).await
}
}
pub(crate) fn planner_decision_action(action: &str) -> bool {

View File

@@ -0,0 +1,102 @@
use axum::body::Body;
use axum::http::Response;
use crate::ai_pipeline::planner::{maybe_build_stream_plan_payload, maybe_build_sync_plan_payload};
use crate::control::GatewayControlDecision;
use crate::execution_runtime::{execute_execution_runtime_stream, execute_execution_runtime_sync};
use crate::{AppState, GatewayControlPlanResponse, GatewayError, GatewayFallbackReason};
pub(crate) async fn maybe_execute_sync_via_plan_fallback(
state: &AppState,
parts: &http::request::Parts,
trace_id: &str,
decision: &GatewayControlDecision,
body_json: &serde_json::Value,
body_base64: Option<String>,
_plan_kind: &str,
_bypass_cache_key: String,
_fallback_reason: GatewayFallbackReason,
) -> Result<Option<Response<Body>>, GatewayError> {
let body_is_empty =
body_base64.is_none() && body_json.as_object().is_some_and(|value| value.is_empty());
let Some(payload) = maybe_build_sync_plan_payload(
state,
parts,
trace_id,
decision,
body_json,
body_base64.as_deref(),
body_is_empty,
)
.await?
else {
return Ok(None);
};
let GatewayControlPlanResponse {
action: _,
plan_kind,
plan,
report_kind,
report_context,
auth_context: _,
} = payload;
let (Some(plan_kind), Some(plan)) = (plan_kind, plan) else {
return Ok(None);
};
execute_execution_runtime_sync(
state,
parts.uri.path(),
plan,
trace_id,
decision,
plan_kind.as_str(),
report_kind,
report_context,
)
.await
}
pub(crate) async fn maybe_execute_stream_via_plan_fallback(
state: &AppState,
parts: &http::request::Parts,
trace_id: &str,
decision: &GatewayControlDecision,
body_json: &serde_json::Value,
body_base64: Option<String>,
_plan_kind: &str,
_bypass_cache_key: String,
_fallback_reason: GatewayFallbackReason,
) -> Result<Option<Response<Body>>, GatewayError> {
let Some(payload) =
maybe_build_stream_plan_payload(state, parts, trace_id, decision, body_json).await?
else {
return Ok(None);
};
let GatewayControlPlanResponse {
action: _,
plan_kind,
plan,
report_kind,
report_context,
auth_context: _,
} = payload;
let (Some(plan_kind), Some(plan)) = (plan_kind, plan) else {
return Ok(None);
};
execute_execution_runtime_stream(
state,
plan,
trace_id,
decision,
plan_kind.as_str(),
report_kind,
report_context,
)
.await
}

View File

@@ -0,0 +1,107 @@
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::time::Duration;
use aether_contracts::ExecutionPlan;
use axum::body::Bytes;
use crate::control::GatewayControlDecision;
use crate::headers::header_value_str;
use crate::provider_transport::provider_types::is_codex_cli_backend_url;
use crate::{AppState, GatewayControlSyncDecisionResponse};
pub(crate) const DIRECT_PLAN_BYPASS_TTL: Duration = Duration::from_secs(30);
pub(crate) const DIRECT_PLAN_BYPASS_MAX_ENTRIES: usize = 512;
pub(crate) fn should_bypass_execution_runtime_decision(
payload: &GatewayControlSyncDecisionResponse,
) -> bool {
let provider_api_format = payload
.provider_api_format
.as_deref()
.map(str::trim)
.unwrap_or_default()
.to_ascii_lowercase();
if !matches!(
provider_api_format.as_str(),
"openai:cli" | "openai:compact"
) {
return false;
}
payload
.upstream_url
.as_deref()
.or(payload.upstream_base_url.as_deref())
.map(str::trim)
.filter(|value| !value.is_empty())
.is_some_and(is_codex_cli_backend_url)
}
pub(crate) fn should_bypass_execution_runtime_plan(plan: &ExecutionPlan) -> bool {
let provider_api_format = plan.provider_api_format.trim().to_ascii_lowercase();
if !matches!(
provider_api_format.as_str(),
"openai:cli" | "openai:compact"
) {
return false;
}
is_codex_cli_backend_url(&plan.url)
}
pub(crate) fn build_direct_plan_bypass_cache_key(
plan_kind: &str,
parts: &http::request::Parts,
body_bytes: &Bytes,
decision: &GatewayControlDecision,
) -> String {
let mut hasher = DefaultHasher::new();
plan_kind.hash(&mut hasher);
parts.method.as_str().hash(&mut hasher);
parts.uri.path().hash(&mut hasher);
parts.uri.query().unwrap_or_default().hash(&mut hasher);
decision
.route_family
.as_deref()
.unwrap_or_default()
.hash(&mut hasher);
decision
.route_kind
.as_deref()
.unwrap_or_default()
.hash(&mut hasher);
decision
.auth_endpoint_signature
.as_deref()
.unwrap_or_default()
.hash(&mut hasher);
header_value_str(&parts.headers, http::header::AUTHORIZATION.as_str())
.unwrap_or_default()
.hash(&mut hasher);
header_value_str(&parts.headers, "x-api-key")
.unwrap_or_default()
.hash(&mut hasher);
header_value_str(&parts.headers, "api-key")
.unwrap_or_default()
.hash(&mut hasher);
header_value_str(&parts.headers, http::header::CONTENT_TYPE.as_str())
.unwrap_or_default()
.hash(&mut hasher);
body_bytes.hash(&mut hasher);
format!("{plan_kind}:{:x}", hasher.finish())
}
pub(crate) fn should_skip_direct_plan(state: &AppState, cache_key: &str) -> bool {
state
.direct_plan_bypass_cache
.should_skip(cache_key, DIRECT_PLAN_BYPASS_TTL)
}
pub(crate) fn mark_direct_plan_bypass(state: &AppState, cache_key: String) {
state.direct_plan_bypass_cache.mark(
cache_key,
DIRECT_PLAN_BYPASS_TTL,
DIRECT_PLAN_BYPASS_MAX_ENTRIES,
);
}

View File

@@ -0,0 +1,27 @@
use axum::body::Body;
use axum::http::Response;
use crate::control::GatewayControlDecision;
use crate::{AppState, GatewayError};
pub(crate) async fn maybe_execute_sync_via_remote_decision(
_state: &AppState,
_parts: &http::request::Parts,
_trace_id: &str,
_decision: &GatewayControlDecision,
_body_json: &serde_json::Value,
_plan_kind: &str,
) -> Result<Option<Response<Body>>, GatewayError> {
Ok(None)
}
pub(crate) async fn maybe_execute_stream_via_remote_decision(
_state: &AppState,
_parts: &http::request::Parts,
_trace_id: &str,
_decision: &GatewayControlDecision,
_body_json: &serde_json::Value,
_plan_kind: &str,
) -> Result<Option<Response<Body>>, GatewayError> {
Ok(None)
}

View File

@@ -0,0 +1,197 @@
use axum::body::{Body, Bytes};
use axum::http::Response;
use std::collections::BTreeMap;
use crate::ai_pipeline::planner::common::OPENAI_VIDEO_CONTENT_PLAN_KIND;
use crate::ai_pipeline::planner::{
is_matching_stream_request, resolve_execution_runtime_stream_plan_kind,
supports_stream_scheduler_decision_kind,
};
use crate::api::response::build_client_response_from_parts;
use crate::control::GatewayControlDecision;
use crate::execution_runtime::execute_execution_runtime_stream;
use crate::{AppState, GatewayError, GatewayFallbackReason};
use super::{
build_direct_plan_bypass_cache_key, maybe_execute_stream_via_local_decision,
maybe_execute_stream_via_local_gemini_files_decision,
maybe_execute_stream_via_local_openai_cli_decision,
maybe_execute_stream_via_local_same_format_provider_decision,
maybe_execute_stream_via_local_standard_decision, maybe_execute_stream_via_plan_fallback,
maybe_execute_stream_via_remote_decision, parse_local_request_body, should_skip_direct_plan,
};
pub(crate) async fn maybe_execute_via_stream_decision_path(
state: &AppState,
parts: &http::request::Parts,
body_bytes: &Bytes,
trace_id: &str,
decision: &GatewayControlDecision,
) -> Result<Option<Response<Body>>, GatewayError> {
let Some(plan_kind) = resolve_execution_runtime_stream_plan_kind(parts, decision) else {
return Ok(None);
};
let Some((body_json, body_base64)) = parse_local_request_body(parts, body_bytes) else {
return Ok(None);
};
if !is_matching_stream_request(plan_kind, parts, &body_json) {
return Ok(None);
}
let bypass_cache_key =
build_direct_plan_bypass_cache_key(plan_kind, parts, body_bytes, decision);
if should_skip_direct_plan(state, &bypass_cache_key) {
return Ok(None);
}
if let Some(response) =
maybe_execute_local_video_task_content_stream(state, parts, trace_id, decision, plan_kind)
.await?
{
return Ok(Some(response));
}
if supports_stream_scheduler_decision_kind(plan_kind) {
if let Some(response) = maybe_execute_stream_via_local_decision(
state, parts, trace_id, decision, &body_json, plan_kind,
)
.await?
{
return Ok(Some(response));
}
if let Some(response) = maybe_execute_stream_via_local_openai_cli_decision(
state, parts, trace_id, decision, &body_json, plan_kind,
)
.await?
{
return Ok(Some(response));
}
if let Some(response) = maybe_execute_stream_via_local_standard_decision(
state, parts, trace_id, decision, &body_json, plan_kind,
)
.await?
{
return Ok(Some(response));
}
if let Some(response) = maybe_execute_stream_via_local_same_format_provider_decision(
state, parts, trace_id, decision, &body_json, plan_kind,
)
.await?
{
return Ok(Some(response));
}
if let Some(response) = maybe_execute_stream_via_local_gemini_files_decision(
state, parts, trace_id, decision, plan_kind,
)
.await?
{
return Ok(Some(response));
}
if let Some(response) = maybe_execute_stream_via_remote_decision(
state, parts, trace_id, decision, &body_json, plan_kind,
)
.await?
{
return Ok(Some(response));
}
}
maybe_execute_stream_via_plan_fallback(
state,
parts,
trace_id,
decision,
&body_json,
body_base64,
plan_kind,
bypass_cache_key,
if supports_stream_scheduler_decision_kind(plan_kind) {
GatewayFallbackReason::RemoteDecisionMiss
} else {
GatewayFallbackReason::SchedulerDecisionUnsupported
},
)
.await
}
async fn maybe_execute_local_video_task_content_stream(
state: &AppState,
parts: &http::request::Parts,
trace_id: &str,
decision: &GatewayControlDecision,
plan_kind: &str,
) -> Result<Option<Response<Body>>, GatewayError> {
if plan_kind != OPENAI_VIDEO_CONTENT_PLAN_KIND
|| decision.route_family.as_deref() != Some("openai")
{
return Ok(None);
}
let _ = state
.hydrate_video_task_for_route(decision.route_family.as_deref(), parts.uri.path())
.await?;
if let Some(task_id) =
crate::video_tasks::extract_openai_task_id_from_content_path(parts.uri.path())
{
let refresh_path = format!("/v1/videos/{task_id}");
if let Some(refresh_plan) = state.video_tasks.prepare_read_refresh_sync_plan(
Some("openai"),
&refresh_path,
trace_id,
) {
state.execute_video_task_refresh_plan(&refresh_plan).await?;
}
}
let Some(action) = state.video_tasks.prepare_openai_content_stream_action(
parts.uri.path(),
parts.uri.query(),
trace_id,
) else {
return Ok(None);
};
match action {
crate::video_tasks::LocalVideoTaskContentAction::Immediate {
status_code,
body_json,
} => Ok(Some(build_json_response(
trace_id,
decision,
status_code,
&body_json,
)?)),
crate::video_tasks::LocalVideoTaskContentAction::StreamPlan(plan) => {
execute_execution_runtime_stream(state, plan, trace_id, decision, plan_kind, None, None)
.await
}
}
}
fn build_json_response(
trace_id: &str,
decision: &GatewayControlDecision,
status_code: u16,
body_json: &serde_json::Value,
) -> Result<Response<Body>, GatewayError> {
let body_bytes =
serde_json::to_vec(body_json).map_err(|err| GatewayError::Internal(err.to_string()))?;
let mut headers = BTreeMap::new();
headers.insert("content-type".to_string(), "application/json".to_string());
headers.insert("content-length".to_string(), body_bytes.len().to_string());
build_client_response_from_parts(
status_code,
&headers,
Body::from(body_bytes),
trace_id,
Some(decision),
)
}

View File

@@ -0,0 +1,261 @@
use axum::body::{Body, Bytes};
use axum::http::Response;
use std::collections::BTreeMap;
use crate::ai_pipeline::planner::common::{
GEMINI_VIDEO_CANCEL_SYNC_PLAN_KIND, OPENAI_VIDEO_CANCEL_SYNC_PLAN_KIND,
OPENAI_VIDEO_DELETE_SYNC_PLAN_KIND, OPENAI_VIDEO_REMIX_SYNC_PLAN_KIND,
};
use crate::ai_pipeline::planner::{
is_matching_stream_request, resolve_execution_runtime_stream_plan_kind,
resolve_execution_runtime_sync_plan_kind, supports_sync_scheduler_decision_kind,
};
use crate::api::response::build_client_response_from_parts;
use crate::control::resolve_execution_runtime_auth_context;
use crate::control::GatewayControlDecision;
use crate::execution_runtime::execute_execution_runtime_sync;
use crate::{AppState, GatewayError, GatewayFallbackReason};
use super::{
build_direct_plan_bypass_cache_key, maybe_execute_sync_via_local_decision,
maybe_execute_sync_via_local_gemini_files_decision,
maybe_execute_sync_via_local_openai_cli_decision,
maybe_execute_sync_via_local_same_format_provider_decision,
maybe_execute_sync_via_local_standard_decision, maybe_execute_sync_via_local_video_decision,
maybe_execute_sync_via_plan_fallback, maybe_execute_sync_via_remote_decision,
parse_local_request_body, should_skip_direct_plan,
};
pub(crate) async fn maybe_execute_via_sync_decision_path(
state: &AppState,
parts: &http::request::Parts,
body_bytes: &Bytes,
trace_id: &str,
decision: &GatewayControlDecision,
) -> Result<Option<Response<Body>>, GatewayError> {
if let Some(response) =
maybe_build_local_video_task_read_response(state, parts, trace_id, decision).await?
{
return Ok(Some(response));
}
let Some(plan_kind) = resolve_execution_runtime_sync_plan_kind(parts, decision) else {
return Ok(None);
};
let Some((body_json, body_base64)) = parse_local_request_body(parts, body_bytes) else {
return Ok(None);
};
if let Some(stream_plan_kind) = resolve_execution_runtime_stream_plan_kind(parts, decision) {
if is_matching_stream_request(stream_plan_kind, parts, &body_json) {
return Ok(None);
}
}
let bypass_cache_key =
build_direct_plan_bypass_cache_key(plan_kind, parts, body_bytes, decision);
if should_skip_direct_plan(state, &bypass_cache_key) {
return Ok(None);
}
if let Some(response) = maybe_execute_local_video_task_follow_up_sync(
state, parts, &body_json, trace_id, decision, plan_kind,
)
.await?
{
return Ok(Some(response));
}
if supports_sync_scheduler_decision_kind(plan_kind) {
if let Some(response) = maybe_execute_sync_via_local_video_decision(
state, parts, &body_json, trace_id, decision, plan_kind,
)
.await?
{
return Ok(Some(response));
}
if let Some(response) = maybe_execute_sync_via_local_decision(
state, parts, trace_id, decision, &body_json, plan_kind,
)
.await?
{
return Ok(Some(response));
}
if let Some(response) = maybe_execute_sync_via_local_openai_cli_decision(
state, parts, trace_id, decision, &body_json, plan_kind,
)
.await?
{
return Ok(Some(response));
}
if let Some(response) = maybe_execute_sync_via_local_standard_decision(
state, parts, trace_id, decision, &body_json, plan_kind,
)
.await?
{
return Ok(Some(response));
}
if let Some(response) = maybe_execute_sync_via_local_same_format_provider_decision(
state, parts, trace_id, decision, &body_json, plan_kind,
)
.await?
{
return Ok(Some(response));
}
if let Some(response) = maybe_execute_sync_via_local_gemini_files_decision(
state,
parts,
&body_json,
body_base64.as_deref(),
body_bytes.is_empty(),
trace_id,
decision,
plan_kind,
)
.await?
{
return Ok(Some(response));
}
if let Some(response) = maybe_execute_sync_via_remote_decision(
state, parts, trace_id, decision, &body_json, plan_kind,
)
.await?
{
return Ok(Some(response));
}
}
maybe_execute_sync_via_plan_fallback(
state,
parts,
trace_id,
decision,
&body_json,
body_base64,
plan_kind,
bypass_cache_key,
if supports_sync_scheduler_decision_kind(plan_kind) {
GatewayFallbackReason::RemoteDecisionMiss
} else {
GatewayFallbackReason::SchedulerDecisionUnsupported
},
)
.await
}
async fn maybe_build_local_video_task_read_response(
state: &AppState,
parts: &http::request::Parts,
trace_id: &str,
decision: &GatewayControlDecision,
) -> Result<Option<Response<Body>>, GatewayError> {
if parts.method != http::Method::GET || decision.route_kind.as_deref() != Some("video") {
return Ok(None);
}
let _ = state
.hydrate_video_task_for_route(decision.route_family.as_deref(), parts.uri.path())
.await?;
let refresh_plan = state.video_tasks.prepare_read_refresh_sync_plan(
decision.route_family.as_deref(),
parts.uri.path(),
trace_id,
);
if let Some(refresh_plan) = refresh_plan {
state.execute_video_task_refresh_plan(&refresh_plan).await?;
}
let read_response = state
.video_tasks
.read_response(decision.route_family.as_deref(), parts.uri.path());
let read_response = match read_response {
Some(read_response) => Some(read_response),
None => {
state
.read_data_backed_video_task_response(
decision.route_family.as_deref(),
parts.uri.path(),
)
.await?
}
};
let Some(read_response) = read_response else {
return Ok(None);
};
let body_bytes = serde_json::to_vec(&read_response.body_json)
.map_err(|err| GatewayError::Internal(err.to_string()))?;
let mut headers = BTreeMap::new();
headers.insert("content-type".to_string(), "application/json".to_string());
headers.insert("content-length".to_string(), body_bytes.len().to_string());
Ok(Some(build_client_response_from_parts(
read_response.status_code,
&headers,
Body::from(body_bytes),
trace_id,
Some(decision),
)?))
}
async fn maybe_execute_local_video_task_follow_up_sync(
state: &AppState,
parts: &http::request::Parts,
body_json: &serde_json::Value,
trace_id: &str,
decision: &GatewayControlDecision,
plan_kind: &str,
) -> Result<Option<Response<Body>>, GatewayError> {
if !matches!(
plan_kind,
OPENAI_VIDEO_REMIX_SYNC_PLAN_KIND
| OPENAI_VIDEO_CANCEL_SYNC_PLAN_KIND
| OPENAI_VIDEO_DELETE_SYNC_PLAN_KIND
| GEMINI_VIDEO_CANCEL_SYNC_PLAN_KIND
) {
return Ok(None);
}
let _ = state
.hydrate_video_task_for_route(decision.route_family.as_deref(), parts.uri.path())
.await?;
let auth_context = resolve_execution_runtime_auth_context(
state,
decision,
&parts.headers,
&parts.uri,
trace_id,
)
.await?;
let Some(follow_up) = state.video_tasks.prepare_follow_up_sync_plan(
plan_kind,
parts.uri.path(),
Some(body_json),
auth_context.as_ref(),
trace_id,
) else {
return Ok(None);
};
execute_execution_runtime_sync(
state,
parts.uri.path(),
follow_up.plan,
trace_id,
decision,
plan_kind,
follow_up.report_kind,
follow_up.report_context,
)
.await
}