refactor: 移除 Python 后端源码,全面迁移至 Rust gateway 架构

- 删除全部 Python 源码 (src/) 及 Alembic 迁移脚本,归档至 _deprecated_py_src/
- 重构 Rust gateway ai_pipeline: 拆分 planner/finalize 模块,新增 contracts/adaptation 层
- 重组 handlers 模块为 admin/public/proxy/internal/shared 子模块结构
- 新增 executor 模块,引入 Rust 原生数据库迁移 (aether-data/migrations)
- 简化 CI/Docker 构建流程,移除 base image 二级构建,统一为单一 app image
- 移除 Python 相关基础设施文件 (entrypoint.sh, gunicorn_conf.py, Dockerfile.base)
This commit is contained in:
fawney19
2026-04-03 16:26:16 +08:00
parent 8f26e1a31f
commit 1d9c77522a
868 changed files with 1735 additions and 2433 deletions

View File

@@ -42,6 +42,8 @@ sqlx.workspace = true
thiserror.workspace = true
tokio.workspace = true
tokio-util.workspace = true
tower = { version = "0.5", features = ["util"] }
tower-http = { version = "0.6", features = ["fs", "compression-gzip", "set-header"] }
tracing.workspace = true
url.workspace = true
uuid.workspace = true

View File

@@ -64,13 +64,13 @@ enum FrameParseError {
Invalid(String),
}
#[path = "stream/decoder.rs"]
#[path = "kiro_stream/stream/decoder.rs"]
mod decoder;
#[path = "stream/state.rs"]
#[path = "kiro_stream/stream/state.rs"]
mod state;
#[path = "stream/util.rs"]
#[path = "kiro_stream/stream/util.rs"]
mod util;
#[cfg(test)]
#[path = "stream/tests.rs"]
#[path = "kiro_stream/stream/tests.rs"]
mod tests;

View File

@@ -0,0 +1,18 @@
pub(crate) mod kiro_stream;
pub(crate) mod private_envelope;
pub(crate) mod surfaces;
pub(crate) use kiro_stream::KiroToClaudeCliStreamState;
pub(crate) use private_envelope::{
maybe_build_provider_private_stream_normalizer,
maybe_normalize_provider_private_sync_report_payload,
normalize_provider_private_report_context, normalize_provider_private_response_value,
provider_private_response_allows_sync_finalize, transform_provider_private_stream_line,
};
pub(crate) use surfaces::{
provider_adaptation_allows_sync_finalize_envelope, provider_adaptation_anchor_api_format,
provider_adaptation_descriptor_for_envelope, provider_adaptation_descriptor_for_provider_type,
provider_adaptation_requires_eventstream_accept,
provider_adaptation_should_unwrap_stream_envelope, ANTIGRAVITY_V1INTERNAL_ENVELOPE_NAME,
GEMINI_CLI_V1INTERNAL_ENVELOPE_NAME,
};

View File

@@ -3,7 +3,7 @@ use std::collections::BTreeMap;
use base64::Engine as _;
use serde_json::Value;
use crate::gateway::ai_pipeline::private_surfaces::{
use crate::gateway::ai_pipeline::adaptation::surfaces::{
provider_adaptation_allows_sync_finalize_envelope, provider_adaptation_descriptor_for_envelope,
provider_adaptation_should_unwrap_stream_envelope, ANTIGRAVITY_V1INTERNAL_ENVELOPE_NAME,
GEMINI_CLI_V1INTERNAL_ENVELOPE_NAME,

View File

@@ -0,0 +1,5 @@
pub(crate) const EXECUTION_RUNTIME_SYNC_ACTION: &str = "execution_runtime_sync";
pub(crate) const EXECUTION_RUNTIME_SYNC_DECISION_ACTION: &str = "execution_runtime_sync_decision";
pub(crate) const EXECUTION_RUNTIME_STREAM_ACTION: &str = "execution_runtime_stream";
pub(crate) const EXECUTION_RUNTIME_STREAM_DECISION_ACTION: &str =
"execution_runtime_stream_decision";

View File

@@ -0,0 +1,43 @@
pub(crate) mod actions;
pub(crate) mod control_payloads;
pub(crate) mod plan_kinds;
pub(crate) mod report_kinds;
pub(crate) use actions::{
EXECUTION_RUNTIME_STREAM_ACTION, EXECUTION_RUNTIME_STREAM_DECISION_ACTION,
EXECUTION_RUNTIME_SYNC_ACTION, EXECUTION_RUNTIME_SYNC_DECISION_ACTION,
};
pub(crate) use control_payloads::{
build_gateway_plan_request, generic_decision_missing_exact_provider_request,
GatewayControlPlanRequest, GatewayControlPlanResponse, GatewayControlSyncDecisionResponse,
};
pub(crate) use plan_kinds::{
CLAUDE_CHAT_STREAM_PLAN_KIND, CLAUDE_CHAT_SYNC_PLAN_KIND, CLAUDE_CLI_STREAM_PLAN_KIND,
CLAUDE_CLI_SYNC_PLAN_KIND, GEMINI_CHAT_STREAM_PLAN_KIND, GEMINI_CHAT_SYNC_PLAN_KIND,
GEMINI_CLI_STREAM_PLAN_KIND, GEMINI_CLI_SYNC_PLAN_KIND, GEMINI_FILES_DELETE_PLAN_KIND,
GEMINI_FILES_DOWNLOAD_PLAN_KIND, GEMINI_FILES_GET_PLAN_KIND, GEMINI_FILES_LIST_PLAN_KIND,
GEMINI_FILES_UPLOAD_PLAN_KIND, GEMINI_VIDEO_CANCEL_SYNC_PLAN_KIND,
GEMINI_VIDEO_CREATE_SYNC_PLAN_KIND, OPENAI_CHAT_STREAM_PLAN_KIND, OPENAI_CHAT_SYNC_PLAN_KIND,
OPENAI_CLI_STREAM_PLAN_KIND, OPENAI_CLI_SYNC_PLAN_KIND, OPENAI_COMPACT_STREAM_PLAN_KIND,
OPENAI_COMPACT_SYNC_PLAN_KIND, OPENAI_VIDEO_CANCEL_SYNC_PLAN_KIND,
OPENAI_VIDEO_CONTENT_PLAN_KIND, OPENAI_VIDEO_CREATE_SYNC_PLAN_KIND,
OPENAI_VIDEO_DELETE_SYNC_PLAN_KIND, OPENAI_VIDEO_REMIX_SYNC_PLAN_KIND,
};
pub(crate) use report_kinds::{
core_error_background_report_kind, core_error_default_client_api_format,
core_success_background_report_kind, implicit_sync_finalize_report_kind,
CLAUDE_CHAT_STREAM_SUCCESS_REPORT_KIND, CLAUDE_CHAT_SYNC_ERROR_REPORT_KIND,
CLAUDE_CHAT_SYNC_FINALIZE_REPORT_KIND, CLAUDE_CHAT_SYNC_SUCCESS_REPORT_KIND,
CLAUDE_CLI_STREAM_SUCCESS_REPORT_KIND, CLAUDE_CLI_SYNC_ERROR_REPORT_KIND,
CLAUDE_CLI_SYNC_FINALIZE_REPORT_KIND, CLAUDE_CLI_SYNC_SUCCESS_REPORT_KIND,
GEMINI_CHAT_STREAM_SUCCESS_REPORT_KIND, GEMINI_CHAT_SYNC_ERROR_REPORT_KIND,
GEMINI_CHAT_SYNC_FINALIZE_REPORT_KIND, GEMINI_CHAT_SYNC_SUCCESS_REPORT_KIND,
GEMINI_CLI_STREAM_SUCCESS_REPORT_KIND, GEMINI_CLI_SYNC_ERROR_REPORT_KIND,
GEMINI_CLI_SYNC_FINALIZE_REPORT_KIND, GEMINI_CLI_SYNC_SUCCESS_REPORT_KIND,
GEMINI_VIDEO_CREATE_SYNC_FINALIZE_REPORT_KIND, OPENAI_CHAT_STREAM_SUCCESS_REPORT_KIND,
OPENAI_CHAT_SYNC_ERROR_REPORT_KIND, OPENAI_CHAT_SYNC_FINALIZE_REPORT_KIND,
OPENAI_CHAT_SYNC_SUCCESS_REPORT_KIND, OPENAI_CLI_STREAM_SUCCESS_REPORT_KIND,
OPENAI_CLI_SYNC_ERROR_REPORT_KIND, OPENAI_CLI_SYNC_FINALIZE_REPORT_KIND,
OPENAI_CLI_SYNC_SUCCESS_REPORT_KIND, OPENAI_COMPACT_SYNC_ERROR_REPORT_KIND,
OPENAI_COMPACT_SYNC_FINALIZE_REPORT_KIND, OPENAI_VIDEO_CREATE_SYNC_FINALIZE_REPORT_KIND,
};

View File

@@ -0,0 +1,26 @@
pub(crate) const GEMINI_FILES_GET_PLAN_KIND: &str = "gemini_files_get";
pub(crate) const GEMINI_FILES_UPLOAD_PLAN_KIND: &str = "gemini_files_upload";
pub(crate) const GEMINI_FILES_LIST_PLAN_KIND: &str = "gemini_files_list";
pub(crate) const GEMINI_FILES_DELETE_PLAN_KIND: &str = "gemini_files_delete";
pub(crate) const GEMINI_FILES_DOWNLOAD_PLAN_KIND: &str = "gemini_files_download";
pub(crate) const OPENAI_VIDEO_CONTENT_PLAN_KIND: &str = "openai_video_content";
pub(crate) const OPENAI_VIDEO_CANCEL_SYNC_PLAN_KIND: &str = "openai_video_cancel_sync";
pub(crate) const OPENAI_VIDEO_REMIX_SYNC_PLAN_KIND: &str = "openai_video_remix_sync";
pub(crate) const OPENAI_VIDEO_DELETE_SYNC_PLAN_KIND: &str = "openai_video_delete_sync";
pub(crate) const GEMINI_VIDEO_CREATE_SYNC_PLAN_KIND: &str = "gemini_video_create_sync";
pub(crate) const GEMINI_VIDEO_CANCEL_SYNC_PLAN_KIND: &str = "gemini_video_cancel_sync";
pub(crate) const OPENAI_CHAT_STREAM_PLAN_KIND: &str = "openai_chat_stream";
pub(crate) const CLAUDE_CHAT_STREAM_PLAN_KIND: &str = "claude_chat_stream";
pub(crate) const GEMINI_CHAT_STREAM_PLAN_KIND: &str = "gemini_chat_stream";
pub(crate) const OPENAI_CLI_STREAM_PLAN_KIND: &str = "openai_cli_stream";
pub(crate) const OPENAI_COMPACT_STREAM_PLAN_KIND: &str = "openai_compact_stream";
pub(crate) const CLAUDE_CLI_STREAM_PLAN_KIND: &str = "claude_cli_stream";
pub(crate) const GEMINI_CLI_STREAM_PLAN_KIND: &str = "gemini_cli_stream";
pub(crate) const OPENAI_VIDEO_CREATE_SYNC_PLAN_KIND: &str = "openai_video_create_sync";
pub(crate) const OPENAI_CHAT_SYNC_PLAN_KIND: &str = "openai_chat_sync";
pub(crate) const OPENAI_CLI_SYNC_PLAN_KIND: &str = "openai_cli_sync";
pub(crate) const OPENAI_COMPACT_SYNC_PLAN_KIND: &str = "openai_compact_sync";
pub(crate) const CLAUDE_CHAT_SYNC_PLAN_KIND: &str = "claude_chat_sync";
pub(crate) const GEMINI_CHAT_SYNC_PLAN_KIND: &str = "gemini_chat_sync";
pub(crate) const CLAUDE_CLI_SYNC_PLAN_KIND: &str = "claude_cli_sync";
pub(crate) const GEMINI_CLI_SYNC_PLAN_KIND: &str = "gemini_cli_sync";

View File

@@ -0,0 +1,92 @@
use super::plan_kinds::{
CLAUDE_CHAT_SYNC_PLAN_KIND, CLAUDE_CLI_SYNC_PLAN_KIND, GEMINI_CHAT_SYNC_PLAN_KIND,
GEMINI_CLI_SYNC_PLAN_KIND, OPENAI_CHAT_SYNC_PLAN_KIND, OPENAI_CLI_SYNC_PLAN_KIND,
OPENAI_COMPACT_SYNC_PLAN_KIND,
};
pub(crate) const OPENAI_CHAT_SYNC_FINALIZE_REPORT_KIND: &str = "openai_chat_sync_finalize";
pub(crate) const CLAUDE_CHAT_SYNC_FINALIZE_REPORT_KIND: &str = "claude_chat_sync_finalize";
pub(crate) const GEMINI_CHAT_SYNC_FINALIZE_REPORT_KIND: &str = "gemini_chat_sync_finalize";
pub(crate) const OPENAI_CLI_SYNC_FINALIZE_REPORT_KIND: &str = "openai_cli_sync_finalize";
pub(crate) const OPENAI_COMPACT_SYNC_FINALIZE_REPORT_KIND: &str = "openai_compact_sync_finalize";
pub(crate) const CLAUDE_CLI_SYNC_FINALIZE_REPORT_KIND: &str = "claude_cli_sync_finalize";
pub(crate) const GEMINI_CLI_SYNC_FINALIZE_REPORT_KIND: &str = "gemini_cli_sync_finalize";
pub(crate) const OPENAI_VIDEO_CREATE_SYNC_FINALIZE_REPORT_KIND: &str =
"openai_video_create_sync_finalize";
pub(crate) const GEMINI_VIDEO_CREATE_SYNC_FINALIZE_REPORT_KIND: &str =
"gemini_video_create_sync_finalize";
pub(crate) const OPENAI_CHAT_SYNC_SUCCESS_REPORT_KIND: &str = "openai_chat_sync_success";
pub(crate) const CLAUDE_CHAT_SYNC_SUCCESS_REPORT_KIND: &str = "claude_chat_sync_success";
pub(crate) const GEMINI_CHAT_SYNC_SUCCESS_REPORT_KIND: &str = "gemini_chat_sync_success";
pub(crate) const OPENAI_CLI_SYNC_SUCCESS_REPORT_KIND: &str = "openai_cli_sync_success";
pub(crate) const CLAUDE_CLI_SYNC_SUCCESS_REPORT_KIND: &str = "claude_cli_sync_success";
pub(crate) const GEMINI_CLI_SYNC_SUCCESS_REPORT_KIND: &str = "gemini_cli_sync_success";
pub(crate) const OPENAI_CHAT_STREAM_SUCCESS_REPORT_KIND: &str = "openai_chat_stream_success";
pub(crate) const CLAUDE_CHAT_STREAM_SUCCESS_REPORT_KIND: &str = "claude_chat_stream_success";
pub(crate) const GEMINI_CHAT_STREAM_SUCCESS_REPORT_KIND: &str = "gemini_chat_stream_success";
pub(crate) const OPENAI_CLI_STREAM_SUCCESS_REPORT_KIND: &str = "openai_cli_stream_success";
pub(crate) const CLAUDE_CLI_STREAM_SUCCESS_REPORT_KIND: &str = "claude_cli_stream_success";
pub(crate) const GEMINI_CLI_STREAM_SUCCESS_REPORT_KIND: &str = "gemini_cli_stream_success";
pub(crate) const OPENAI_CHAT_SYNC_ERROR_REPORT_KIND: &str = "openai_chat_sync_error";
pub(crate) const CLAUDE_CHAT_SYNC_ERROR_REPORT_KIND: &str = "claude_chat_sync_error";
pub(crate) const GEMINI_CHAT_SYNC_ERROR_REPORT_KIND: &str = "gemini_chat_sync_error";
pub(crate) const OPENAI_CLI_SYNC_ERROR_REPORT_KIND: &str = "openai_cli_sync_error";
pub(crate) const OPENAI_COMPACT_SYNC_ERROR_REPORT_KIND: &str = "openai_compact_sync_error";
pub(crate) const CLAUDE_CLI_SYNC_ERROR_REPORT_KIND: &str = "claude_cli_sync_error";
pub(crate) const GEMINI_CLI_SYNC_ERROR_REPORT_KIND: &str = "gemini_cli_sync_error";
pub(crate) fn implicit_sync_finalize_report_kind(plan_kind: &str) -> Option<&'static str> {
match plan_kind {
OPENAI_CHAT_SYNC_PLAN_KIND => Some(OPENAI_CHAT_SYNC_FINALIZE_REPORT_KIND),
CLAUDE_CHAT_SYNC_PLAN_KIND => Some(CLAUDE_CHAT_SYNC_FINALIZE_REPORT_KIND),
GEMINI_CHAT_SYNC_PLAN_KIND => Some(GEMINI_CHAT_SYNC_FINALIZE_REPORT_KIND),
OPENAI_CLI_SYNC_PLAN_KIND => Some(OPENAI_CLI_SYNC_FINALIZE_REPORT_KIND),
OPENAI_COMPACT_SYNC_PLAN_KIND => Some(OPENAI_COMPACT_SYNC_FINALIZE_REPORT_KIND),
CLAUDE_CLI_SYNC_PLAN_KIND => Some(CLAUDE_CLI_SYNC_FINALIZE_REPORT_KIND),
GEMINI_CLI_SYNC_PLAN_KIND => Some(GEMINI_CLI_SYNC_FINALIZE_REPORT_KIND),
_ => None,
}
}
pub(crate) fn core_error_default_client_api_format(report_kind: &str) -> Option<&'static str> {
match report_kind {
OPENAI_CHAT_SYNC_FINALIZE_REPORT_KIND => Some("openai:chat"),
CLAUDE_CHAT_SYNC_FINALIZE_REPORT_KIND => Some("claude:chat"),
GEMINI_CHAT_SYNC_FINALIZE_REPORT_KIND => Some("gemini:chat"),
OPENAI_CLI_SYNC_FINALIZE_REPORT_KIND => Some("openai:cli"),
OPENAI_COMPACT_SYNC_FINALIZE_REPORT_KIND => Some("openai:compact"),
CLAUDE_CLI_SYNC_FINALIZE_REPORT_KIND => Some("claude:cli"),
GEMINI_CLI_SYNC_FINALIZE_REPORT_KIND => Some("gemini:cli"),
_ => None,
}
}
pub(crate) fn core_error_background_report_kind(report_kind: &str) -> Option<&'static str> {
match report_kind {
OPENAI_CHAT_SYNC_FINALIZE_REPORT_KIND => Some(OPENAI_CHAT_SYNC_ERROR_REPORT_KIND),
CLAUDE_CHAT_SYNC_FINALIZE_REPORT_KIND => Some(CLAUDE_CHAT_SYNC_ERROR_REPORT_KIND),
GEMINI_CHAT_SYNC_FINALIZE_REPORT_KIND => Some(GEMINI_CHAT_SYNC_ERROR_REPORT_KIND),
OPENAI_CLI_SYNC_FINALIZE_REPORT_KIND => Some(OPENAI_CLI_SYNC_ERROR_REPORT_KIND),
OPENAI_COMPACT_SYNC_FINALIZE_REPORT_KIND => Some(OPENAI_COMPACT_SYNC_ERROR_REPORT_KIND),
CLAUDE_CLI_SYNC_FINALIZE_REPORT_KIND => Some(CLAUDE_CLI_SYNC_ERROR_REPORT_KIND),
GEMINI_CLI_SYNC_FINALIZE_REPORT_KIND => Some(GEMINI_CLI_SYNC_ERROR_REPORT_KIND),
_ => None,
}
}
pub(crate) fn core_success_background_report_kind(report_kind: &str) -> Option<&'static str> {
match report_kind {
OPENAI_CHAT_SYNC_FINALIZE_REPORT_KIND => Some(OPENAI_CHAT_SYNC_SUCCESS_REPORT_KIND),
CLAUDE_CHAT_SYNC_FINALIZE_REPORT_KIND => Some(CLAUDE_CHAT_SYNC_SUCCESS_REPORT_KIND),
GEMINI_CHAT_SYNC_FINALIZE_REPORT_KIND => Some(GEMINI_CHAT_SYNC_SUCCESS_REPORT_KIND),
OPENAI_CLI_SYNC_FINALIZE_REPORT_KIND | OPENAI_COMPACT_SYNC_FINALIZE_REPORT_KIND => {
Some(OPENAI_CLI_SYNC_SUCCESS_REPORT_KIND)
}
CLAUDE_CLI_SYNC_FINALIZE_REPORT_KIND => Some(CLAUDE_CLI_SYNC_SUCCESS_REPORT_KIND),
GEMINI_CLI_SYNC_FINALIZE_REPORT_KIND => Some(GEMINI_CLI_SYNC_SUCCESS_REPORT_KIND),
_ => None,
}
}

View File

@@ -1,5 +1,12 @@
use serde_json::{Map, Value};
#[cfg(test)]
use crate::gateway::ai_pipeline::contracts::core_success_background_report_kind as contract_core_success_background_report_kind;
use crate::gateway::ai_pipeline::contracts::{
core_error_background_report_kind as contract_core_error_background_report_kind,
core_error_default_client_api_format as contract_core_error_default_client_api_format,
};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum LocalCoreSyncErrorKind {
InvalidRequest,
@@ -17,44 +24,16 @@ pub(crate) fn is_core_error_finalize_kind(report_kind: &str) -> bool {
}
pub(crate) fn core_error_default_client_api_format(report_kind: &str) -> Option<&'static str> {
match report_kind {
"openai_chat_sync_finalize" => Some("openai:chat"),
"claude_chat_sync_finalize" => Some("claude:chat"),
"gemini_chat_sync_finalize" => Some("gemini:chat"),
"openai_cli_sync_finalize" => Some("openai:cli"),
"openai_compact_sync_finalize" => Some("openai:compact"),
"claude_cli_sync_finalize" => Some("claude:cli"),
"gemini_cli_sync_finalize" => Some("gemini:cli"),
_ => None,
}
contract_core_error_default_client_api_format(report_kind)
}
pub(crate) fn core_error_background_report_kind(report_kind: &str) -> Option<&'static str> {
match report_kind {
"openai_chat_sync_finalize" => Some("openai_chat_sync_error"),
"claude_chat_sync_finalize" => Some("claude_chat_sync_error"),
"gemini_chat_sync_finalize" => Some("gemini_chat_sync_error"),
"openai_cli_sync_finalize" => Some("openai_cli_sync_error"),
"openai_compact_sync_finalize" => Some("openai_compact_sync_error"),
"claude_cli_sync_finalize" => Some("claude_cli_sync_error"),
"gemini_cli_sync_finalize" => Some("gemini_cli_sync_error"),
_ => None,
}
contract_core_error_background_report_kind(report_kind)
}
#[cfg(test)]
pub(crate) fn core_success_background_report_kind(report_kind: &str) -> Option<&'static str> {
match report_kind {
"openai_chat_sync_finalize" => Some("openai_chat_sync_success"),
"claude_chat_sync_finalize" => Some("claude_chat_sync_success"),
"gemini_chat_sync_finalize" => Some("gemini_chat_sync_success"),
"openai_cli_sync_finalize" | "openai_compact_sync_finalize" => {
Some("openai_cli_sync_success")
}
"claude_cli_sync_finalize" => Some("claude_cli_sync_success"),
"gemini_cli_sync_finalize" => Some("gemini_cli_sync_success"),
_ => None,
}
contract_core_success_background_report_kind(report_kind)
}
pub(crate) fn build_core_error_body_for_client_format(

View File

@@ -1,8 +1,8 @@
use serde_json::{json, Map, Value};
use uuid::Uuid;
use super::shared::parse_openai_tool_arguments;
use super::super::to_openai_chat::{extract_openai_text_content, parse_openai_tool_result_content};
use super::shared::parse_openai_tool_arguments;
use crate::gateway::ai_pipeline::planner::standard::{
copy_request_number_field, map_openai_reasoning_effort_to_claude_output,
parse_openai_stop_sequences, resolve_openai_chat_max_tokens,
@@ -34,8 +34,10 @@ pub(crate) fn convert_openai_chat_request_to_claude_request(
}
}
"user" => {
let blocks =
convert_openai_content_to_claude_blocks(message_object.get("content"), true)?;
let blocks = convert_openai_content_to_claude_blocks(
message_object.get("content"),
true,
)?;
if !blocks.is_empty() {
messages.push(build_claude_message("user", blocks));
}

View File

@@ -3,8 +3,8 @@ use std::collections::BTreeMap;
use serde_json::{json, Map, Value};
use uuid::Uuid;
use super::shared::parse_openai_tool_arguments;
use super::super::to_openai_chat::{extract_openai_text_content, parse_openai_tool_result_content};
use super::shared::parse_openai_tool_arguments;
use crate::gateway::ai_pipeline::planner::standard::{
copy_request_number_field_as, map_openai_reasoning_effort_to_gemini_budget,
parse_openai_stop_sequences, value_as_u64,
@@ -37,8 +37,10 @@ pub(crate) fn convert_openai_chat_request_to_gemini_request(
}
}
"user" => {
let parts =
convert_openai_content_to_gemini_parts(message_object.get("content"), true)?;
let parts = convert_openai_content_to_gemini_parts(
message_object.get("content"),
true,
)?;
if !parts.is_empty() {
contents.push(json!({
"role": "user",

View File

@@ -1,8 +1,6 @@
use serde_json::{json, Value};
use super::shared::{
build_openai_cli_response, canonicalize_tool_arguments,
};
use super::shared::{build_openai_cli_response, canonicalize_tool_arguments};
pub(crate) fn convert_openai_chat_response_to_openai_cli(
body_json: &Value,

View File

@@ -1,7 +1,7 @@
use serde_json::{json, Value};
use super::shared::{build_generated_tool_call_id, canonicalize_tool_arguments};
use super::super::from_openai_chat::build_openai_cli_response;
use super::shared::{build_generated_tool_call_id, canonicalize_tool_arguments};
pub(crate) fn convert_claude_cli_response_to_openai_cli(
body_json: &Value,

View File

@@ -1,7 +1,7 @@
use serde_json::{json, Value};
use super::shared::{build_generated_tool_call_id, canonicalize_tool_arguments};
use super::super::from_openai_chat::build_openai_cli_response;
use super::shared::{build_generated_tool_call_id, canonicalize_tool_arguments};
pub(crate) fn convert_gemini_cli_response_to_openai_cli(
body_json: &Value,

View File

@@ -4,14 +4,13 @@ use axum::body::Body;
use axum::http::Response;
use serde_json::Value;
use crate::gateway::ai_pipeline::contracts::core_success_background_report_kind;
pub(crate) use crate::gateway::ai_pipeline::runtime::{
normalize_provider_private_response_value as unwrap_local_finalize_response_value,
provider_private_response_allows_sync_finalize as local_finalize_allows_envelope,
};
use crate::gateway::{
build_client_response_from_parts, GatewayControlDecision, GatewayError,
GatewaySyncReportRequest,
};
use crate::gateway::api::response::build_client_response_from_parts;
use crate::gateway::{GatewayControlDecision, GatewayError, GatewaySyncReportRequest};
pub(crate) struct LocalCoreSyncFinalizeOutcome {
pub(crate) response: Response<Body>,
@@ -127,17 +126,7 @@ fn map_local_finalize_to_success_report(
}
fn map_local_finalize_kind_to_success_report_kind(report_kind: &str) -> Option<&'static str> {
match report_kind {
"openai_chat_sync_finalize" => Some("openai_chat_sync_success"),
"claude_chat_sync_finalize" => Some("claude_chat_sync_success"),
"gemini_chat_sync_finalize" => Some("gemini_chat_sync_success"),
"openai_cli_sync_finalize" | "openai_compact_sync_finalize" => {
Some("openai_cli_sync_success")
}
"claude_cli_sync_finalize" => Some("claude_cli_sync_success"),
"gemini_cli_sync_finalize" => Some("gemini_cli_sync_success"),
_ => None,
}
core_success_background_report_kind(report_kind)
}
pub(crate) fn canonicalize_tool_arguments(value: Option<Value>) -> String {

View File

@@ -1,11 +1,11 @@
use serde_json::Value;
use crate::gateway::ai_pipeline::adaptation::private_envelope::transform_provider_private_stream_line as transform_envelope_line;
use crate::gateway::ai_pipeline::finalize::sse::{encode_done_sse, encode_json_sse};
use crate::gateway::ai_pipeline::finalize::standard::{
BufferedCliConversionStreamState, BufferedStandardConversionStreamState,
ClaudeToOpenAIChatStreamState, GeminiToOpenAIChatStreamState, OpenAICliToOpenAIChatStreamState,
};
use crate::gateway::ai_pipeline::finalize::sse::{encode_done_sse, encode_json_sse};
use crate::gateway::ai_pipeline::private_response::transform_provider_private_stream_line as transform_envelope_line;
use crate::gateway::ai_pipeline::runtime::KiroToClaudeCliStreamState;
use crate::gateway::GatewayError;

View File

@@ -8,10 +8,8 @@ use serde_json::{json, Map, Value};
use crate::gateway::ai_pipeline::conversion::{
sync_chat_response_conversion_kind, sync_cli_response_conversion_kind,
};
use crate::gateway::{
build_client_response_from_parts, GatewayControlDecision, GatewayError,
GatewaySyncReportRequest,
};
use crate::gateway::api::response::build_client_response_from_parts;
use crate::gateway::{GatewayControlDecision, GatewayError, GatewaySyncReportRequest};
pub(crate) use crate::gateway::ai_pipeline::finalize::common::{
build_generated_tool_call_id, build_local_success_outcome,
@@ -47,7 +45,7 @@ pub(crate) fn maybe_build_local_core_sync_finalize_response(
payload: &GatewaySyncReportRequest,
) -> Result<Option<LocalCoreSyncFinalizeOutcome>, GatewayError> {
let Some(normalized_payload) =
crate::gateway::ai_pipeline::private_response::maybe_normalize_provider_private_sync_report_payload(payload)?
crate::gateway::ai_pipeline::adaptation::private_envelope::maybe_normalize_provider_private_sync_report_payload(payload)?
else {
return Ok(None);
};

View File

@@ -1,13 +1,14 @@
pub(crate) mod common;
pub(crate) mod sse;
pub(crate) mod standard;
pub(crate) use crate::gateway::api::response::{
build_client_response, build_client_response_from_parts,
};
pub(crate) use crate::gateway::execution_runtime::maybe_build_local_sync_finalize_response;
pub(crate) use common::build_local_success_outcome;
pub(crate) use internal::{
maybe_build_stream_response_rewriter, maybe_build_sync_finalize_outcome,
maybe_compile_sync_finalize_response, LocalCoreSyncFinalizeOutcome,
};
pub(crate) use crate::gateway::build_client_response;
pub(crate) use crate::gateway::build_client_response_from_parts;
pub(crate) use crate::gateway::execution_runtime::maybe_build_local_sync_finalize_response;
pub(crate) mod internal;

View File

@@ -0,0 +1,16 @@
use serde_json::Value;
use crate::gateway::ai_pipeline::finalize::common::LocalCoreSyncFinalizeOutcome;
use crate::gateway::{GatewayControlDecision, GatewayError, GatewaySyncReportRequest};
pub(crate) fn aggregate_claude_stream_sync_response(body: &[u8]) -> Option<Value> {
super::claude_chat::aggregate_claude_stream_sync_response(body)
}
pub(crate) fn maybe_build_local_claude_stream_sync_response(
trace_id: &str,
decision: &GatewayControlDecision,
payload: &GatewaySyncReportRequest,
) -> Result<Option<LocalCoreSyncFinalizeOutcome>, GatewayError> {
super::claude_chat::maybe_build_local_claude_stream_sync_response(trace_id, decision, payload)
}

View File

@@ -0,0 +1,12 @@
use crate::gateway::ai_pipeline::finalize::common::LocalCoreSyncFinalizeOutcome;
use crate::gateway::{GatewayControlDecision, GatewayError, GatewaySyncReportRequest};
pub(crate) fn maybe_build_local_claude_cli_stream_sync_response(
trace_id: &str,
decision: &GatewayControlDecision,
payload: &GatewaySyncReportRequest,
) -> Result<Option<LocalCoreSyncFinalizeOutcome>, GatewayError> {
super::claude_cli::maybe_build_local_claude_cli_stream_sync_response(
trace_id, decision, payload,
)
}

View File

@@ -0,0 +1,16 @@
use serde_json::Value;
use crate::gateway::ai_pipeline::finalize::common::LocalCoreSyncFinalizeOutcome;
use crate::gateway::{GatewayControlDecision, GatewayError, GatewaySyncReportRequest};
pub(crate) fn aggregate_gemini_stream_sync_response(body: &[u8]) -> Option<Value> {
super::gemini_chat::aggregate_gemini_stream_sync_response(body)
}
pub(crate) fn maybe_build_local_gemini_stream_sync_response(
trace_id: &str,
decision: &GatewayControlDecision,
payload: &GatewaySyncReportRequest,
) -> Result<Option<LocalCoreSyncFinalizeOutcome>, GatewayError> {
super::gemini_chat::maybe_build_local_gemini_stream_sync_response(trace_id, decision, payload)
}

View File

@@ -0,0 +1,12 @@
use crate::gateway::ai_pipeline::finalize::common::LocalCoreSyncFinalizeOutcome;
use crate::gateway::{GatewayControlDecision, GatewayError, GatewaySyncReportRequest};
pub(crate) fn maybe_build_local_gemini_cli_stream_sync_response(
trace_id: &str,
decision: &GatewayControlDecision,
payload: &GatewaySyncReportRequest,
) -> Result<Option<LocalCoreSyncFinalizeOutcome>, GatewayError> {
super::gemini_cli::maybe_build_local_gemini_cli_stream_sync_response(
trace_id, decision, payload,
)
}

View File

@@ -9,12 +9,20 @@ use crate::gateway::{GatewayControlDecision, GatewayError, GatewaySyncReportRequ
#[path = "claude/chat.rs"]
mod claude_chat;
#[path = "claude/chat_stream.rs"]
mod claude_chat_stream;
#[path = "claude/cli.rs"]
mod claude_cli;
#[path = "claude/cli_stream.rs"]
mod claude_cli_stream;
#[path = "gemini/chat.rs"]
mod gemini_chat;
#[path = "gemini/chat_stream.rs"]
mod gemini_chat_stream;
#[path = "gemini/cli.rs"]
mod gemini_cli;
#[path = "gemini/cli_stream.rs"]
mod gemini_cli_stream;
#[path = "openai/chat.rs"]
mod openai_chat;
#[path = "openai/chat_stream.rs"]
@@ -24,8 +32,8 @@ mod openai_cli;
#[path = "openai/cli_stream.rs"]
mod openai_cli_stream;
#[path = "stream.rs"]
mod stream;
#[path = "stream_common.rs"]
mod stream_common;
pub(crate) use crate::gateway::ai_pipeline::conversion::response::{
build_openai_cli_response, convert_claude_chat_response_to_openai_chat,
@@ -34,18 +42,19 @@ pub(crate) use crate::gateway::ai_pipeline::conversion::response::{
convert_openai_chat_response_to_gemini_chat, convert_openai_chat_response_to_openai_cli,
convert_openai_cli_response_to_openai_chat,
};
pub(crate) use claude_chat::{
pub(crate) use claude_chat::maybe_build_local_claude_sync_response;
pub(crate) use claude_chat_stream::{
aggregate_claude_stream_sync_response, maybe_build_local_claude_stream_sync_response,
maybe_build_local_claude_sync_response,
};
pub(crate) use claude_cli::maybe_build_local_claude_cli_stream_sync_response;
pub(crate) use gemini_chat::{
pub(crate) use claude_cli_stream::maybe_build_local_claude_cli_stream_sync_response;
pub(crate) use gemini_chat::maybe_build_local_gemini_sync_response;
pub(crate) use gemini_chat_stream::{
aggregate_gemini_stream_sync_response, maybe_build_local_gemini_stream_sync_response,
maybe_build_local_gemini_sync_response,
};
pub(crate) use gemini_cli::maybe_build_local_gemini_cli_stream_sync_response;
pub(crate) use gemini_cli_stream::maybe_build_local_gemini_cli_stream_sync_response;
pub(crate) use openai_chat::{
aggregate_openai_chat_stream_sync_response, maybe_build_local_openai_chat_cross_format_stream_sync_response,
aggregate_openai_chat_stream_sync_response,
maybe_build_local_openai_chat_cross_format_stream_sync_response,
maybe_build_local_openai_chat_cross_format_sync_response,
maybe_build_local_openai_chat_stream_sync_response,
maybe_build_local_openai_chat_sync_response,
@@ -54,12 +63,13 @@ pub(crate) use openai_chat_stream::{
ClaudeToOpenAIChatStreamState, GeminiToOpenAIChatStreamState, OpenAICliToOpenAIChatStreamState,
};
pub(crate) use openai_cli::{
aggregate_openai_cli_stream_sync_response, maybe_build_local_openai_cli_cross_format_stream_sync_response,
aggregate_openai_cli_stream_sync_response,
maybe_build_local_openai_cli_cross_format_stream_sync_response,
maybe_build_local_openai_cli_cross_format_sync_response,
maybe_build_local_openai_cli_stream_sync_response,
};
pub(crate) use openai_cli_stream::BufferedCliConversionStreamState;
pub(crate) use stream::BufferedStandardConversionStreamState;
pub(crate) use stream_common::BufferedStandardConversionStreamState;
pub(crate) fn aggregate_standard_chat_stream_sync_response(
body: &[u8],

View File

@@ -6,13 +6,13 @@ use super::{
aggregate_openai_cli_stream_sync_response, convert_claude_chat_response_to_openai_chat,
convert_gemini_chat_response_to_openai_chat,
};
use crate::gateway::ai_pipeline::conversion::sync_chat_response_conversion_kind;
use crate::gateway::ai_pipeline::finalize::common::{
build_generated_tool_call_id, build_local_success_outcome,
build_local_success_outcome_with_conversion_report, canonicalize_tool_arguments,
local_finalize_allows_envelope, unwrap_local_finalize_response_value,
LocalCoreSyncFinalizeOutcome,
};
use crate::gateway::ai_pipeline::conversion::sync_chat_response_conversion_kind;
#[derive(Debug, Default)]
struct OpenAIChatChoiceState {

View File

@@ -5,12 +5,12 @@ use super::{
aggregate_claude_stream_sync_response, aggregate_gemini_stream_sync_response,
convert_claude_cli_response_to_openai_cli, convert_gemini_cli_response_to_openai_cli,
};
use crate::gateway::ai_pipeline::conversion::sync_cli_response_conversion_kind;
use crate::gateway::ai_pipeline::finalize::common::{
build_local_success_outcome, build_local_success_outcome_with_conversion_report,
canonicalize_tool_arguments, local_finalize_allows_envelope,
unwrap_local_finalize_response_value, LocalCoreSyncFinalizeOutcome,
};
use crate::gateway::ai_pipeline::conversion::sync_cli_response_conversion_kind;
pub(crate) fn maybe_build_local_openai_cli_stream_sync_response(
trace_id: &str,

View File

@@ -1,12 +1,12 @@
use serde_json::{json, Value};
use crate::gateway::ai_pipeline::adaptation::private_envelope::transform_provider_private_stream_line as transform_envelope_line;
use crate::gateway::ai_pipeline::adaptation::surfaces::provider_adaptation_should_unwrap_stream_envelope;
use crate::gateway::ai_pipeline::finalize::sse::{encode_done_sse, encode_json_sse};
use crate::gateway::ai_pipeline::finalize::standard::{
aggregate_standard_chat_stream_sync_response, aggregate_standard_cli_stream_sync_response,
convert_standard_chat_response, convert_standard_cli_response,
};
use crate::gateway::ai_pipeline::finalize::sse::{encode_done_sse, encode_json_sse};
use crate::gateway::ai_pipeline::private_response::transform_provider_private_stream_line as transform_envelope_line;
use crate::gateway::ai_pipeline::private_surfaces::provider_adaptation_should_unwrap_stream_envelope;
use crate::gateway::GatewayError;
#[derive(Default)]

View File

@@ -1,6 +1,6 @@
pub(crate) mod adaptation;
pub(crate) mod contracts;
pub(crate) mod conversion;
pub(crate) mod finalize;
pub(crate) mod planner;
pub(crate) mod private_response;
pub(crate) mod private_surfaces;
pub(crate) mod runtime;

View File

@@ -1,40 +1,22 @@
use axum::body::Bytes;
use base64::Engine as _;
pub(crate) use crate::gateway::ai_pipeline::contracts::{
CLAUDE_CHAT_STREAM_PLAN_KIND, CLAUDE_CHAT_SYNC_PLAN_KIND, CLAUDE_CLI_STREAM_PLAN_KIND,
CLAUDE_CLI_SYNC_PLAN_KIND, EXECUTION_RUNTIME_STREAM_ACTION,
EXECUTION_RUNTIME_STREAM_DECISION_ACTION, EXECUTION_RUNTIME_SYNC_ACTION,
EXECUTION_RUNTIME_SYNC_DECISION_ACTION, GEMINI_CHAT_STREAM_PLAN_KIND,
GEMINI_CHAT_SYNC_PLAN_KIND, GEMINI_CLI_STREAM_PLAN_KIND, GEMINI_CLI_SYNC_PLAN_KIND,
GEMINI_FILES_DELETE_PLAN_KIND, GEMINI_FILES_DOWNLOAD_PLAN_KIND, GEMINI_FILES_GET_PLAN_KIND,
GEMINI_FILES_LIST_PLAN_KIND, GEMINI_FILES_UPLOAD_PLAN_KIND, GEMINI_VIDEO_CANCEL_SYNC_PLAN_KIND,
GEMINI_VIDEO_CREATE_SYNC_PLAN_KIND, OPENAI_CHAT_STREAM_PLAN_KIND, OPENAI_CHAT_SYNC_PLAN_KIND,
OPENAI_CLI_STREAM_PLAN_KIND, OPENAI_CLI_SYNC_PLAN_KIND, OPENAI_COMPACT_STREAM_PLAN_KIND,
OPENAI_COMPACT_SYNC_PLAN_KIND, OPENAI_VIDEO_CANCEL_SYNC_PLAN_KIND,
OPENAI_VIDEO_CONTENT_PLAN_KIND, OPENAI_VIDEO_CREATE_SYNC_PLAN_KIND,
OPENAI_VIDEO_DELETE_SYNC_PLAN_KIND, OPENAI_VIDEO_REMIX_SYNC_PLAN_KIND,
};
use crate::gateway::headers::is_json_request;
pub(crate) const GEMINI_FILES_GET_PLAN_KIND: &str = "gemini_files_get";
pub(crate) const GEMINI_FILES_UPLOAD_PLAN_KIND: &str = "gemini_files_upload";
pub(crate) const GEMINI_FILES_LIST_PLAN_KIND: &str = "gemini_files_list";
pub(crate) const GEMINI_FILES_DELETE_PLAN_KIND: &str = "gemini_files_delete";
pub(crate) const GEMINI_FILES_DOWNLOAD_PLAN_KIND: &str = "gemini_files_download";
pub(crate) const OPENAI_VIDEO_CONTENT_PLAN_KIND: &str = "openai_video_content";
pub(crate) const OPENAI_VIDEO_CANCEL_SYNC_PLAN_KIND: &str = "openai_video_cancel_sync";
pub(crate) const OPENAI_VIDEO_REMIX_SYNC_PLAN_KIND: &str = "openai_video_remix_sync";
pub(crate) const OPENAI_VIDEO_DELETE_SYNC_PLAN_KIND: &str = "openai_video_delete_sync";
pub(crate) const GEMINI_VIDEO_CREATE_SYNC_PLAN_KIND: &str = "gemini_video_create_sync";
pub(crate) const GEMINI_VIDEO_CANCEL_SYNC_PLAN_KIND: &str = "gemini_video_cancel_sync";
pub(crate) const OPENAI_CHAT_STREAM_PLAN_KIND: &str = "openai_chat_stream";
pub(crate) const CLAUDE_CHAT_STREAM_PLAN_KIND: &str = "claude_chat_stream";
pub(crate) const GEMINI_CHAT_STREAM_PLAN_KIND: &str = "gemini_chat_stream";
pub(crate) const OPENAI_CLI_STREAM_PLAN_KIND: &str = "openai_cli_stream";
pub(crate) const OPENAI_COMPACT_STREAM_PLAN_KIND: &str = "openai_compact_stream";
pub(crate) const CLAUDE_CLI_STREAM_PLAN_KIND: &str = "claude_cli_stream";
pub(crate) const GEMINI_CLI_STREAM_PLAN_KIND: &str = "gemini_cli_stream";
pub(crate) const OPENAI_VIDEO_CREATE_SYNC_PLAN_KIND: &str = "openai_video_create_sync";
pub(crate) const OPENAI_CHAT_SYNC_PLAN_KIND: &str = "openai_chat_sync";
pub(crate) const OPENAI_CLI_SYNC_PLAN_KIND: &str = "openai_cli_sync";
pub(crate) const OPENAI_COMPACT_SYNC_PLAN_KIND: &str = "openai_compact_sync";
pub(crate) const CLAUDE_CHAT_SYNC_PLAN_KIND: &str = "claude_chat_sync";
pub(crate) const GEMINI_CHAT_SYNC_PLAN_KIND: &str = "gemini_chat_sync";
pub(crate) const CLAUDE_CLI_SYNC_PLAN_KIND: &str = "claude_cli_sync";
pub(crate) const GEMINI_CLI_SYNC_PLAN_KIND: &str = "gemini_cli_sync";
pub(crate) const EXECUTION_RUNTIME_SYNC_ACTION: &str = "execution_runtime_sync";
pub(crate) const EXECUTION_RUNTIME_SYNC_DECISION_ACTION: &str = "execution_runtime_sync_decision";
pub(crate) const EXECUTION_RUNTIME_STREAM_ACTION: &str = "execution_runtime_stream";
pub(crate) const EXECUTION_RUNTIME_STREAM_DECISION_ACTION: &str =
"execution_runtime_stream_decision";
pub(crate) fn parse_direct_request_body(
parts: &http::request::Parts,
body_bytes: &Bytes,

View File

@@ -1,25 +0,0 @@
use axum::body::{Body, Bytes};
use axum::http::Response;
use crate::gateway::intent;
use crate::gateway::{AppState, GatewayControlDecision, GatewayError};
pub(crate) async fn maybe_execute_sync_local_path(
state: &AppState,
parts: &http::request::Parts,
body_bytes: &Bytes,
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
}
pub(crate) async fn maybe_execute_stream_local_path(
state: &AppState,
parts: &http::request::Parts,
body_bytes: &Bytes,
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
}

View File

@@ -2,10 +2,7 @@ use crate::gateway::{AppState, GatewayControlDecision, GatewayError};
pub(crate) mod candidate_affinity;
pub(crate) mod common;
pub(crate) mod contracts;
mod decision;
pub(crate) mod family_core;
pub(crate) mod local_path;
pub(crate) mod passthrough;
pub(crate) mod plan_builders;
pub(crate) mod specialized;
@@ -26,7 +23,7 @@ pub(crate) use self::common::{
OPENAI_VIDEO_CONTENT_PLAN_KIND, OPENAI_VIDEO_CREATE_SYNC_PLAN_KIND,
OPENAI_VIDEO_DELETE_SYNC_PLAN_KIND, OPENAI_VIDEO_REMIX_SYNC_PLAN_KIND,
};
pub(crate) use self::contracts::{
pub(crate) use crate::gateway::ai_pipeline::contracts::{
build_gateway_plan_request, generic_decision_missing_exact_provider_request,
GatewayControlPlanRequest, GatewayControlPlanResponse, GatewayControlSyncDecisionResponse,
};
@@ -43,18 +40,12 @@ pub(crate) use crate::gateway::scheduler::{
pub(crate) use passthrough::{
maybe_build_stream_local_same_format_provider_decision_payload,
maybe_build_sync_local_same_format_provider_decision_payload,
maybe_execute_stream_via_local_same_format_provider_decision,
maybe_execute_sync_via_local_same_format_provider_decision,
};
pub(crate) use specialized::{
maybe_build_stream_local_gemini_files_decision_payload,
maybe_build_sync_local_gemini_files_decision_payload,
maybe_build_sync_local_video_decision_payload,
maybe_execute_stream_via_local_gemini_files_decision,
maybe_execute_sync_via_local_gemini_files_decision,
maybe_execute_sync_via_local_video_decision,
};
pub(crate) use local_path::{maybe_execute_stream_local_path, maybe_execute_sync_local_path};
pub(crate) use standard::{
copy_request_number_field, copy_request_number_field_as,
map_openai_reasoning_effort_to_claude_output, map_openai_reasoning_effort_to_gemini_budget,
@@ -62,11 +53,7 @@ pub(crate) use standard::{
maybe_build_stream_local_openai_cli_decision_payload,
maybe_build_stream_local_standard_decision_payload, maybe_build_sync_local_decision_payload,
maybe_build_sync_local_openai_cli_decision_payload,
maybe_build_sync_local_standard_decision_payload, maybe_execute_stream_via_local_decision,
maybe_execute_stream_via_local_openai_cli_decision,
maybe_execute_stream_via_local_standard_decision, maybe_execute_sync_via_local_decision,
maybe_execute_sync_via_local_openai_cli_decision,
maybe_execute_sync_via_local_standard_decision, parse_openai_stop_sequences,
maybe_build_sync_local_standard_decision_payload, parse_openai_stop_sequences,
resolve_openai_chat_max_tokens, value_as_u64,
};

View File

@@ -1,11 +1,9 @@
//! Requests that can stay in the same public/provider contract family.
mod provider;
pub(crate) mod provider;
pub(crate) use self::provider::{
maybe_build_stream_local_same_format_provider_decision_payload,
maybe_build_sync_local_same_format_provider_decision_payload,
maybe_execute_stream_via_local_same_format_provider_decision,
maybe_execute_sync_via_local_same_format_provider_decision,
};
pub(crate) use crate::gateway::provider_transport::provider_type_supports_local_same_format_transport;

View File

@@ -8,6 +8,13 @@ use serde_json::{json, Value};
use tracing::warn;
use uuid::Uuid;
use crate::gateway::ai_pipeline::planner::plan_builders::{
LocalStreamPlanAndReport, LocalSyncPlanAndReport,
};
use crate::gateway::ai_pipeline::planner::prefer_local_tunnel_owner_candidates;
use crate::gateway::ai_pipeline::planner::{
EXECUTION_RUNTIME_STREAM_DECISION_ACTION, EXECUTION_RUNTIME_SYNC_DECISION_ACTION,
};
use crate::gateway::headers::collect_control_headers;
use crate::gateway::provider_transport::{
apply_local_body_rules, apply_local_header_rules, build_antigravity_safe_v1internal_request,
@@ -29,27 +36,17 @@ use crate::gateway::provider_transport::{
AntigravityRequestEnvelopeSupport, AntigravityRequestSideSupport, AntigravityRequestUrlAction,
LocalResolvedOAuthRequestAuth, KIRO_ENVELOPE_NAME,
};
use crate::gateway::request_candidates::{
current_unix_secs, record_local_request_candidate_status,
};
use crate::gateway::ai_pipeline::planner::plan_builders::{
LocalStreamPlanAndReport, LocalSyncPlanAndReport,
};
use crate::gateway::ai_pipeline::planner::prefer_local_tunnel_owner_candidates;
use crate::gateway::scheduler::{
list_selectable_candidates, GatewayMinimalCandidateSelectionCandidate,
current_unix_secs, list_selectable_candidates, record_local_request_candidate_status,
GatewayMinimalCandidateSelectionCandidate,
};
use crate::gateway::{
append_execution_contract_fields_to_value, execute_execution_runtime_stream,
execute_execution_runtime_sync, AppState, ConversionMode, ExecutionStrategy,
append_execution_contract_fields_to_value, AppState, ConversionMode, ExecutionStrategy,
GatewayControlDecision, GatewayControlSyncDecisionResponse, GatewayError,
};
use crate::gateway::ai_pipeline::planner::{
EXECUTION_RUNTIME_STREAM_DECISION_ACTION, EXECUTION_RUNTIME_SYNC_DECISION_ACTION,
};
mod family;
mod plans;
pub(crate) mod family;
pub(crate) mod plans;
mod request;
pub(super) use self::family::{
@@ -61,12 +58,6 @@ pub(super) use self::family::{
pub(crate) use self::family::{
maybe_build_stream_local_same_format_provider_decision_payload,
maybe_build_sync_local_same_format_provider_decision_payload,
maybe_execute_stream_via_local_same_format_provider_decision,
maybe_execute_sync_via_local_same_format_provider_decision,
};
use self::plans::{
build_local_stream_plan_and_reports, build_local_sync_plan_and_reports, resolve_stream_spec,
resolve_sync_spec,
};
use self::request::{
build_same_format_provider_request_body, build_same_format_upstream_url,

View File

@@ -1,69 +1,14 @@
use axum::body::Body;
use axum::http::Response;
use crate::gateway::{
AppState, GatewayControlDecision, GatewayControlSyncDecisionResponse, GatewayError,
};
use crate::gateway::ai_pipeline::planner::family_core::{
execute_stream_plan_and_reports, execute_sync_plan_and_reports,
};
use crate::gateway::ai_pipeline::planner::plan_builders::{
LocalStreamPlanAndReport, LocalSyncPlanAndReport,
};
use crate::gateway::{AppState, GatewayControlDecision, GatewayControlSyncDecisionResponse, GatewayError};
use super::super::plans::{
build_local_stream_plan_and_reports, build_local_sync_plan_and_reports, resolve_stream_spec,
resolve_sync_spec,
};
use super::super::plans::{resolve_stream_spec, resolve_sync_spec};
use super::candidates::{
materialize_local_same_format_provider_candidate_attempts,
resolve_local_same_format_provider_decision_input,
};
use super::payload::maybe_build_local_same_format_provider_decision_payload_for_candidate;
pub(crate) async fn maybe_execute_sync_via_local_same_format_provider_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> {
let Some(spec) = resolve_sync_spec(plan_kind) else {
return Ok(None);
};
let plan_and_reports =
build_local_sync_plan_and_reports(state, parts, trace_id, decision, body_json, spec)
.await?;
if plan_and_reports.is_empty() {
return Ok(None);
}
execute_sync_plan_and_reports(state, parts, trace_id, decision, plan_kind, plan_and_reports)
.await
}
pub(crate) async fn maybe_execute_stream_via_local_same_format_provider_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> {
let Some(spec) = resolve_stream_spec(plan_kind) else {
return Ok(None);
};
let plan_and_reports =
build_local_stream_plan_and_reports(state, parts, trace_id, decision, body_json, spec)
.await?;
if plan_and_reports.is_empty() {
return Ok(None);
}
execute_stream_plan_and_reports(state, trace_id, decision, plan_kind, plan_and_reports).await
}
pub(crate) async fn maybe_build_sync_local_same_format_provider_decision_payload(
state: &AppState,
parts: &http::request::Parts,

View File

@@ -3,8 +3,7 @@ use serde_json::json;
use tracing::warn;
use uuid::Uuid;
use crate::gateway::request_candidates::current_unix_secs;
use crate::gateway::scheduler::list_selectable_candidates;
use crate::gateway::scheduler::{current_unix_secs, list_selectable_candidates};
use crate::gateway::{
append_execution_contract_fields_to_value, AppState, ConversionMode, ExecutionStrategy,
GatewayControlDecision, GatewayError,
@@ -84,9 +83,10 @@ pub(crate) async fn materialize_local_same_format_provider_candidate_attempts(
current_unix_secs(),
)
.await?;
let candidates =
crate::gateway::ai_pipeline::planner::prefer_local_tunnel_owner_candidates(state, candidates)
.await;
let candidates = crate::gateway::ai_pipeline::planner::prefer_local_tunnel_owner_candidates(
state, candidates,
)
.await;
let created_at_unix_secs = current_unix_secs();
let mut attempts = Vec::with_capacity(candidates.len());

View File

@@ -1,17 +1,15 @@
mod build;
mod candidates;
mod execute;
mod payload;
mod types;
pub(crate) use self::build::{
maybe_build_stream_local_same_format_provider_decision_payload,
maybe_build_sync_local_same_format_provider_decision_payload,
};
pub(crate) use self::candidates::{
materialize_local_same_format_provider_candidate_attempts,
resolve_local_same_format_provider_decision_input,
};
pub(crate) use self::execute::{
maybe_build_stream_local_same_format_provider_decision_payload,
maybe_build_sync_local_same_format_provider_decision_payload,
maybe_execute_stream_via_local_same_format_provider_decision,
maybe_execute_sync_via_local_same_format_provider_decision,
};
pub(crate) use self::payload::maybe_build_local_same_format_provider_decision_payload_for_candidate;
pub(crate) use self::types::{LocalSameFormatProviderFamily, LocalSameFormatProviderSpec};

View File

@@ -21,8 +21,7 @@ use crate::gateway::provider_transport::{
AntigravityRequestEnvelopeSupport, AntigravityRequestSideSupport,
LocalResolvedOAuthRequestAuth, KIRO_ENVELOPE_NAME,
};
use crate::gateway::request_candidates::current_unix_secs;
use crate::gateway::scheduler::GatewayMinimalCandidateSelectionCandidate;
use crate::gateway::scheduler::{current_unix_secs, GatewayMinimalCandidateSelectionCandidate};
use crate::gateway::{
append_execution_contract_fields_to_value, AppState, ConversionMode, ExecutionStrategy,
GatewayControlSyncDecisionResponse, EXECUTION_RUNTIME_STREAM_DECISION_ACTION,

View File

@@ -17,7 +17,7 @@ use crate::gateway::ai_pipeline::planner::{
GEMINI_CLI_STREAM_PLAN_KIND, GEMINI_CLI_SYNC_PLAN_KIND,
};
pub(super) fn resolve_sync_spec(plan_kind: &str) -> Option<LocalSameFormatProviderSpec> {
pub(crate) fn resolve_sync_spec(plan_kind: &str) -> Option<LocalSameFormatProviderSpec> {
match plan_kind {
CLAUDE_CHAT_SYNC_PLAN_KIND => Some(LocalSameFormatProviderSpec {
api_format: "claude:chat",
@@ -51,7 +51,7 @@ pub(super) fn resolve_sync_spec(plan_kind: &str) -> Option<LocalSameFormatProvid
}
}
pub(super) fn resolve_stream_spec(plan_kind: &str) -> Option<LocalSameFormatProviderSpec> {
pub(crate) fn resolve_stream_spec(plan_kind: &str) -> Option<LocalSameFormatProviderSpec> {
match plan_kind {
CLAUDE_CHAT_STREAM_PLAN_KIND => Some(LocalSameFormatProviderSpec {
api_format: "claude:chat",
@@ -85,7 +85,7 @@ pub(super) fn resolve_stream_spec(plan_kind: &str) -> Option<LocalSameFormatProv
}
}
pub(super) async fn build_local_sync_plan_and_reports(
pub(crate) async fn build_local_sync_plan_and_reports(
state: &AppState,
parts: &http::request::Parts,
trace_id: &str,
@@ -141,7 +141,7 @@ pub(super) async fn build_local_sync_plan_and_reports(
Ok(plans)
}
pub(super) async fn build_local_stream_plan_and_reports(
pub(crate) async fn build_local_stream_plan_and_reports(
state: &AppState,
parts: &http::request::Parts,
trace_id: &str,

View File

@@ -1,12 +1,20 @@
use std::collections::BTreeMap;
use aether_data::repository::candidates::{RequestCandidateStatus, UpsertRequestCandidateRecord};
use axum::body::Body;
use axum::http::Response;
use serde_json::json;
use tracing::warn;
use uuid::Uuid;
use crate::gateway::ai_pipeline::planner::plan_builders::{
build_passthrough_stream_plan_from_decision, build_passthrough_sync_plan_from_decision,
LocalStreamPlanAndReport, LocalSyncPlanAndReport,
};
use crate::gateway::ai_pipeline::planner::prefer_local_tunnel_owner_candidates;
use crate::gateway::ai_pipeline::planner::{
EXECUTION_RUNTIME_STREAM_DECISION_ACTION, EXECUTION_RUNTIME_SYNC_DECISION_ACTION,
GEMINI_FILES_DELETE_PLAN_KIND, GEMINI_FILES_DOWNLOAD_PLAN_KIND, GEMINI_FILES_GET_PLAN_KIND,
GEMINI_FILES_LIST_PLAN_KIND, GEMINI_FILES_UPLOAD_PLAN_KIND,
};
use crate::gateway::headers::collect_control_headers;
use crate::gateway::provider_transport::{
apply_local_body_rules, apply_local_header_rules, build_gemini_files_passthrough_url,
@@ -14,26 +22,12 @@ use crate::gateway::provider_transport::{
resolve_transport_execution_timeouts, resolve_transport_proxy_snapshot_with_tunnel_affinity,
resolve_transport_tls_profile, supports_local_gemini_transport_with_network,
};
use crate::gateway::request_candidates::{
current_unix_secs, record_local_request_candidate_status,
};
use crate::gateway::ai_pipeline::planner::plan_builders::{
build_passthrough_stream_plan_from_decision, build_passthrough_sync_plan_from_decision,
LocalStreamPlanAndReport, LocalSyncPlanAndReport,
};
use crate::gateway::ai_pipeline::planner::prefer_local_tunnel_owner_candidates;
use crate::gateway::scheduler::{
list_selectable_candidates_for_required_capability_without_requested_model,
GatewayMinimalCandidateSelectionCandidate,
current_unix_secs, list_selectable_candidates_for_required_capability_without_requested_model,
record_local_request_candidate_status, GatewayMinimalCandidateSelectionCandidate,
};
use crate::gateway::{
execute_execution_runtime_stream, execute_execution_runtime_sync, AppState,
GatewayControlDecision, GatewayControlSyncDecisionResponse, GatewayError,
};
use crate::gateway::ai_pipeline::planner::{
EXECUTION_RUNTIME_STREAM_DECISION_ACTION, EXECUTION_RUNTIME_SYNC_DECISION_ACTION,
GEMINI_FILES_DELETE_PLAN_KIND, GEMINI_FILES_DOWNLOAD_PLAN_KIND, GEMINI_FILES_GET_PLAN_KIND,
GEMINI_FILES_LIST_PLAN_KIND, GEMINI_FILES_UPLOAD_PLAN_KIND,
AppState, GatewayControlDecision, GatewayControlSyncDecisionResponse, GatewayError,
};
const GEMINI_FILES_CANDIDATE_API_FORMAT: &str = "gemini:chat";
@@ -60,7 +54,7 @@ struct LocalGeminiFilesCandidateAttempt {
candidate_id: String,
}
pub(crate) async fn maybe_execute_sync_via_local_gemini_files_decision(
pub(crate) async fn build_local_gemini_files_sync_plan_and_reports_for_kind(
state: &AppState,
parts: &http::request::Parts,
body_json: &serde_json::Value,
@@ -69,12 +63,12 @@ pub(crate) async fn maybe_execute_sync_via_local_gemini_files_decision(
trace_id: &str,
decision: &GatewayControlDecision,
plan_kind: &str,
) -> Result<Option<Response<Body>>, GatewayError> {
) -> Result<Vec<LocalSyncPlanAndReport>, GatewayError> {
let Some(spec) = resolve_sync_spec(plan_kind) else {
return Ok(None);
return Ok(Vec::new());
};
let plan_and_reports = build_local_sync_plan_and_reports(
build_local_sync_plan_and_reports(
state,
parts,
body_json,
@@ -84,69 +78,21 @@ pub(crate) async fn maybe_execute_sync_via_local_gemini_files_decision(
decision,
spec,
)
.await?;
if plan_and_reports.is_empty() {
return Ok(None);
}
let mut remaining = plan_and_reports.into_iter();
while let Some(plan_and_report) = remaining.next() {
if let Some(response) = execute_execution_runtime_sync(
state,
parts.uri.path(),
plan_and_report.plan,
trace_id,
decision,
plan_kind,
plan_and_report.report_kind,
plan_and_report.report_context,
)
.await?
{
mark_unused_local_files_candidates(state, remaining.collect()).await;
return Ok(Some(response));
}
}
Ok(None)
.await
}
pub(crate) async fn maybe_execute_stream_via_local_gemini_files_decision(
pub(crate) async fn build_local_gemini_files_stream_plan_and_reports_for_kind(
state: &AppState,
parts: &http::request::Parts,
trace_id: &str,
decision: &GatewayControlDecision,
plan_kind: &str,
) -> Result<Option<Response<Body>>, GatewayError> {
) -> Result<Vec<LocalStreamPlanAndReport>, GatewayError> {
let Some(spec) = resolve_stream_spec(plan_kind) else {
return Ok(None);
return Ok(Vec::new());
};
let plan_and_reports =
build_local_stream_plan_and_reports(state, parts, trace_id, decision, spec).await?;
if plan_and_reports.is_empty() {
return Ok(None);
}
let mut remaining = plan_and_reports.into_iter();
while let Some(plan_and_report) = remaining.next() {
if let Some(response) = execute_execution_runtime_stream(
state,
plan_and_report.plan,
trace_id,
decision,
plan_kind,
plan_and_report.report_kind,
plan_and_report.report_context,
)
.await?
{
mark_unused_local_files_candidates(state, remaining.collect()).await;
return Ok(Some(response));
}
}
Ok(None)
build_local_stream_plan_and_reports(state, parts, trace_id, decision, spec).await
}
pub(crate) async fn maybe_build_sync_local_gemini_files_decision_payload(

View File

@@ -1,14 +1,10 @@
//! Non-matrix AI surfaces such as files and video.
mod files;
mod video;
pub(crate) mod files;
pub(crate) mod video;
pub(crate) use self::files::{
maybe_build_stream_local_gemini_files_decision_payload,
maybe_build_sync_local_gemini_files_decision_payload,
maybe_execute_stream_via_local_gemini_files_decision,
maybe_execute_sync_via_local_gemini_files_decision,
};
pub(crate) use self::video::{
maybe_build_sync_local_video_decision_payload, maybe_execute_sync_via_local_video_decision,
};
pub(crate) use self::video::maybe_build_sync_local_video_decision_payload;

View File

@@ -1,12 +1,18 @@
use std::collections::BTreeMap;
use aether_data::repository::candidates::{RequestCandidateStatus, UpsertRequestCandidateRecord};
use axum::body::Body;
use axum::http::Response;
use serde_json::{json, Value};
use tracing::warn;
use uuid::Uuid;
use crate::gateway::ai_pipeline::planner::plan_builders::{
build_passthrough_sync_plan_from_decision, LocalSyncPlanAndReport,
};
use crate::gateway::ai_pipeline::planner::prefer_local_tunnel_owner_candidates;
use crate::gateway::ai_pipeline::planner::{
EXECUTION_RUNTIME_SYNC_DECISION_ACTION, GEMINI_VIDEO_CREATE_SYNC_PLAN_KIND,
OPENAI_VIDEO_CREATE_SYNC_PLAN_KIND,
};
use crate::gateway::headers::collect_control_headers;
use crate::gateway::provider_transport::{
apply_local_body_rules, apply_local_header_rules, build_gemini_video_predict_long_running_url,
@@ -15,23 +21,12 @@ use crate::gateway::provider_transport::{
resolve_transport_proxy_snapshot_with_tunnel_affinity, resolve_transport_tls_profile,
supports_local_gemini_transport_with_network, supports_local_standard_transport_with_network,
};
use crate::gateway::request_candidates::{
current_unix_secs, record_local_request_candidate_status,
};
use crate::gateway::ai_pipeline::planner::plan_builders::{
build_passthrough_sync_plan_from_decision, LocalSyncPlanAndReport,
};
use crate::gateway::ai_pipeline::planner::prefer_local_tunnel_owner_candidates;
use crate::gateway::scheduler::{
list_selectable_candidates, GatewayMinimalCandidateSelectionCandidate,
current_unix_secs, list_selectable_candidates, record_local_request_candidate_status,
GatewayMinimalCandidateSelectionCandidate,
};
use crate::gateway::{
execute_execution_runtime_sync, AppState, GatewayControlDecision,
GatewayControlSyncDecisionResponse, GatewayError,
};
use crate::gateway::ai_pipeline::planner::{
EXECUTION_RUNTIME_SYNC_DECISION_ACTION, GEMINI_VIDEO_CREATE_SYNC_PLAN_KIND,
OPENAI_VIDEO_CREATE_SYNC_PLAN_KIND,
AppState, GatewayControlDecision, GatewayControlSyncDecisionResponse, GatewayError,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -62,45 +57,19 @@ struct LocalVideoCreateCandidateAttempt {
candidate_id: String,
}
pub(crate) async fn maybe_execute_sync_via_local_video_decision(
pub(crate) async fn build_local_video_sync_plan_and_reports_for_kind(
state: &AppState,
parts: &http::request::Parts,
body_json: &serde_json::Value,
trace_id: &str,
decision: &GatewayControlDecision,
plan_kind: &str,
) -> Result<Option<Response<Body>>, GatewayError> {
) -> Result<Vec<LocalSyncPlanAndReport>, GatewayError> {
let Some(spec) = resolve_sync_spec(plan_kind) else {
return Ok(None);
return Ok(Vec::new());
};
let plan_and_reports =
build_local_sync_plan_and_reports(state, parts, body_json, trace_id, decision, spec)
.await?;
if plan_and_reports.is_empty() {
return Ok(None);
}
let mut remaining = plan_and_reports.into_iter();
while let Some(plan_and_report) = remaining.next() {
if let Some(response) = execute_execution_runtime_sync(
state,
parts.uri.path(),
plan_and_report.plan,
trace_id,
decision,
plan_kind,
plan_and_report.report_kind,
plan_and_report.report_context,
)
.await?
{
mark_unused_local_video_candidates(state, remaining.collect()).await;
return Ok(Some(response));
}
}
Ok(None)
build_local_sync_plan_and_reports(state, parts, body_json, trace_id, decision, spec).await
}
pub(crate) async fn maybe_build_sync_local_video_decision_payload(

View File

@@ -4,7 +4,7 @@ use crate::gateway::ai_pipeline::planner::{
use super::super::family::{LocalStandardSourceFamily, LocalStandardSourceMode, LocalStandardSpec};
pub(super) fn resolve_sync_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
pub(crate) fn resolve_sync_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
match plan_kind {
CLAUDE_CHAT_SYNC_PLAN_KIND => Some(LocalStandardSpec {
api_format: "claude:chat",
@@ -18,7 +18,7 @@ pub(super) fn resolve_sync_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
}
}
pub(super) fn resolve_stream_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
pub(crate) fn resolve_stream_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
match plan_kind {
CLAUDE_CHAT_STREAM_PLAN_KIND => Some(LocalStandardSpec {
api_format: "claude:chat",

View File

@@ -4,7 +4,7 @@ use crate::gateway::ai_pipeline::planner::{
use super::super::family::{LocalStandardSourceFamily, LocalStandardSourceMode, LocalStandardSpec};
pub(super) fn resolve_sync_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
pub(crate) fn resolve_sync_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
match plan_kind {
CLAUDE_CLI_SYNC_PLAN_KIND => Some(LocalStandardSpec {
api_format: "claude:cli",
@@ -18,7 +18,7 @@ pub(super) fn resolve_sync_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
}
}
pub(super) fn resolve_stream_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
pub(crate) fn resolve_stream_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
match plan_kind {
CLAUDE_CLI_STREAM_PLAN_KIND => Some(LocalStandardSpec {
api_format: "claude:cli",

View File

@@ -1,63 +1,14 @@
use axum::body::Body;
use axum::http::Response;
use crate::gateway::{
AppState, GatewayControlDecision, GatewayControlSyncDecisionResponse, GatewayError,
};
use super::family::{
maybe_build_stream_via_standard_family_payload, maybe_build_sync_via_standard_family_payload,
maybe_execute_stream_via_standard_family_decision,
maybe_execute_sync_via_standard_family_decision,
};
pub(crate) use crate::gateway::ai_pipeline::conversion::request::normalize_claude_request_to_openai_chat_request;
mod chat;
mod cli;
pub(crate) async fn maybe_execute_sync_via_local_claude_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> {
maybe_execute_sync_via_standard_family_decision(
state,
parts,
trace_id,
decision,
body_json,
plan_kind,
|plan_kind| {
chat::resolve_sync_spec(plan_kind).or_else(|| cli::resolve_sync_spec(plan_kind))
},
)
.await
}
pub(crate) async fn maybe_execute_stream_via_local_claude_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> {
maybe_execute_stream_via_standard_family_decision(
state,
parts,
trace_id,
decision,
body_json,
plan_kind,
|plan_kind| {
chat::resolve_stream_spec(plan_kind).or_else(|| cli::resolve_stream_spec(plan_kind))
},
)
.await
}
pub(crate) mod chat;
pub(crate) mod cli;
pub(crate) async fn maybe_build_sync_local_claude_decision_payload(
state: &AppState,

View File

@@ -1,16 +1,13 @@
use axum::body::Body;
use axum::http::Response;
use tracing::warn;
use crate::gateway::ai_pipeline::planner::family_core::{
execute_stream_plan_and_reports, execute_sync_plan_and_reports,
};
use crate::gateway::ai_pipeline::planner::plan_builders::{
build_gemini_stream_plan_from_decision, build_gemini_sync_plan_from_decision,
build_standard_stream_plan_from_decision, build_standard_sync_plan_from_decision,
LocalStreamPlanAndReport, LocalSyncPlanAndReport,
};
use crate::gateway::{AppState, GatewayControlDecision, GatewayControlSyncDecisionResponse, GatewayError};
use crate::gateway::{
AppState, GatewayControlDecision, GatewayControlSyncDecisionResponse, GatewayError,
};
use super::candidates::{
materialize_local_standard_candidate_attempts, resolve_local_standard_decision_input,
@@ -18,53 +15,6 @@ use super::candidates::{
use super::payload::maybe_build_local_standard_decision_payload_for_candidate;
use super::types::{LocalStandardSourceFamily, LocalStandardSpec};
pub(crate) async fn maybe_execute_sync_via_standard_family_decision(
state: &AppState,
parts: &http::request::Parts,
trace_id: &str,
decision: &GatewayControlDecision,
body_json: &serde_json::Value,
plan_kind: &str,
resolve_sync_spec: fn(&str) -> Option<LocalStandardSpec>,
) -> Result<Option<Response<Body>>, GatewayError> {
let Some(spec) = resolve_sync_spec(plan_kind) else {
return Ok(None);
};
let plan_and_reports =
build_local_sync_plan_and_reports(state, parts, trace_id, decision, body_json, spec)
.await?;
if plan_and_reports.is_empty() {
return Ok(None);
}
execute_sync_plan_and_reports(state, parts, trace_id, decision, plan_kind, plan_and_reports)
.await
}
pub(crate) async fn maybe_execute_stream_via_standard_family_decision(
state: &AppState,
parts: &http::request::Parts,
trace_id: &str,
decision: &GatewayControlDecision,
body_json: &serde_json::Value,
plan_kind: &str,
resolve_stream_spec: fn(&str) -> Option<LocalStandardSpec>,
) -> Result<Option<Response<Body>>, GatewayError> {
let Some(spec) = resolve_stream_spec(plan_kind) else {
return Ok(None);
};
let plan_and_reports =
build_local_stream_plan_and_reports(state, parts, trace_id, decision, body_json, spec)
.await?;
if plan_and_reports.is_empty() {
return Ok(None);
}
execute_stream_plan_and_reports(state, trace_id, decision, plan_kind, plan_and_reports).await
}
pub(crate) async fn maybe_build_sync_via_standard_family_payload(
state: &AppState,
parts: &http::request::Parts,
@@ -137,7 +87,7 @@ pub(crate) async fn maybe_build_stream_via_standard_family_payload(
Ok(None)
}
async fn build_local_sync_plan_and_reports(
pub(crate) async fn build_local_sync_plan_and_reports(
state: &AppState,
parts: &http::request::Parts,
trace_id: &str,
@@ -186,7 +136,7 @@ async fn build_local_sync_plan_and_reports(
Ok(plans)
}
async fn build_local_stream_plan_and_reports(
pub(crate) async fn build_local_stream_plan_and_reports(
state: &AppState,
parts: &http::request::Parts,
trace_id: &str,

View File

@@ -5,9 +5,8 @@ use serde_json::json;
use tracing::warn;
use uuid::Uuid;
use crate::gateway::request_candidates::current_unix_secs;
use crate::gateway::scheduler::{
list_selectable_candidates, GatewayMinimalCandidateSelectionCandidate,
current_unix_secs, list_selectable_candidates, GatewayMinimalCandidateSelectionCandidate,
};
use crate::gateway::{
append_execution_contract_fields_to_value, AppState, ConversionMode, ExecutionStrategy,
@@ -118,9 +117,10 @@ pub(super) async fn materialize_local_standard_candidate_attempts(
}
}
}
let candidates =
crate::gateway::ai_pipeline::planner::prefer_local_tunnel_owner_candidates(state, candidates)
.await;
let candidates = crate::gateway::ai_pipeline::planner::prefer_local_tunnel_owner_candidates(
state, candidates,
)
.await;
let created_at_unix_secs = current_unix_secs();
let mut attempts = Vec::with_capacity(candidates.len());

View File

@@ -1,12 +1,11 @@
mod build;
mod candidates;
mod execute;
mod payload;
mod types;
pub(crate) use self::execute::{
pub(crate) use self::build::{
build_local_stream_plan_and_reports, build_local_sync_plan_and_reports,
maybe_build_stream_via_standard_family_payload, maybe_build_sync_via_standard_family_payload,
maybe_execute_stream_via_standard_family_decision,
maybe_execute_sync_via_standard_family_decision,
};
pub(crate) use self::types::{
LocalStandardSourceFamily, LocalStandardSourceMode, LocalStandardSpec,

View File

@@ -10,7 +10,7 @@ use crate::gateway::provider_transport::{
resolve_transport_execution_timeouts, resolve_transport_proxy_snapshot_with_tunnel_affinity,
resolve_transport_tls_profile, LocalResolvedOAuthRequestAuth,
};
use crate::gateway::request_candidates::current_unix_secs;
use crate::gateway::scheduler::current_unix_secs;
use crate::gateway::{
append_execution_contract_fields_to_value, AppState, ConversionMode, ExecutionStrategy,
GatewayControlSyncDecisionResponse, EXECUTION_RUNTIME_STREAM_DECISION_ACTION,

View File

@@ -4,7 +4,7 @@ use crate::gateway::ai_pipeline::planner::{
use super::super::family::{LocalStandardSourceFamily, LocalStandardSourceMode, LocalStandardSpec};
pub(super) fn resolve_sync_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
pub(crate) fn resolve_sync_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
match plan_kind {
GEMINI_CHAT_SYNC_PLAN_KIND => Some(LocalStandardSpec {
api_format: "gemini:chat",
@@ -18,7 +18,7 @@ pub(super) fn resolve_sync_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
}
}
pub(super) fn resolve_stream_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
pub(crate) fn resolve_stream_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
match plan_kind {
GEMINI_CHAT_STREAM_PLAN_KIND => Some(LocalStandardSpec {
api_format: "gemini:chat",

View File

@@ -4,7 +4,7 @@ use crate::gateway::ai_pipeline::planner::{
use super::super::family::{LocalStandardSourceFamily, LocalStandardSourceMode, LocalStandardSpec};
pub(super) fn resolve_sync_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
pub(crate) fn resolve_sync_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
match plan_kind {
GEMINI_CLI_SYNC_PLAN_KIND => Some(LocalStandardSpec {
api_format: "gemini:cli",
@@ -18,7 +18,7 @@ pub(super) fn resolve_sync_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
}
}
pub(super) fn resolve_stream_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
pub(crate) fn resolve_stream_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
match plan_kind {
GEMINI_CLI_STREAM_PLAN_KIND => Some(LocalStandardSpec {
api_format: "gemini:cli",

View File

@@ -1,63 +1,14 @@
use axum::body::Body;
use axum::http::Response;
use crate::gateway::{
AppState, GatewayControlDecision, GatewayControlSyncDecisionResponse, GatewayError,
};
use super::family::{
maybe_build_stream_via_standard_family_payload, maybe_build_sync_via_standard_family_payload,
maybe_execute_stream_via_standard_family_decision,
maybe_execute_sync_via_standard_family_decision,
};
pub(crate) use crate::gateway::ai_pipeline::conversion::request::normalize_gemini_request_to_openai_chat_request;
mod chat;
mod cli;
pub(crate) async fn maybe_execute_sync_via_local_gemini_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> {
maybe_execute_sync_via_standard_family_decision(
state,
parts,
trace_id,
decision,
body_json,
plan_kind,
|plan_kind| {
chat::resolve_sync_spec(plan_kind).or_else(|| cli::resolve_sync_spec(plan_kind))
},
)
.await
}
pub(crate) async fn maybe_execute_stream_via_local_gemini_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> {
maybe_execute_stream_via_standard_family_decision(
state,
parts,
trace_id,
decision,
body_json,
plan_kind,
|plan_kind| {
chat::resolve_stream_spec(plan_kind).or_else(|| cli::resolve_stream_spec(plan_kind))
},
)
.await
}
pub(crate) mod chat;
pub(crate) mod cli;
pub(crate) async fn maybe_build_sync_local_gemini_decision_payload(
state: &AppState,

View File

@@ -1,88 +1,51 @@
//! Standard contract planning surface.
//!
//! This groups the public standard matrix in one place:
//! request-side conversion, matrix registry, and local standard execution entrypoints.
use axum::body::Body;
use axum::http::Response;
//! This groups the standard planning surface in one place:
//! request-side conversion, matrix registry, and decision payload builders.
use crate::gateway::{
AppState, GatewayControlDecision, GatewayControlSyncDecisionResponse, GatewayError,
};
pub(crate) mod claude;
mod family;
pub(crate) mod family;
pub(crate) mod gemini;
mod matrix;
pub(crate) mod openai;
mod normalize;
pub(crate) mod openai_chat;
pub(crate) mod openai_cli;
pub(crate) use self::matrix::{
build_standard_request_body, build_standard_upstream_url,
normalize_standard_request_to_openai_chat_request,
};
pub(crate) use self::normalize::{
build_cross_format_openai_chat_request_body, build_cross_format_openai_chat_upstream_url,
build_cross_format_openai_cli_request_body, build_cross_format_openai_cli_upstream_url,
build_local_openai_chat_request_body, build_local_openai_chat_upstream_url,
build_local_openai_cli_request_body, build_local_openai_cli_upstream_url,
};
pub(crate) use self::openai_chat::{
copy_request_number_field, copy_request_number_field_as,
map_openai_reasoning_effort_to_claude_output, map_openai_reasoning_effort_to_gemini_budget,
maybe_build_stream_local_decision_payload, maybe_build_sync_local_decision_payload,
parse_openai_stop_sequences, resolve_openai_chat_max_tokens, value_as_u64,
};
pub(crate) use self::openai_cli::{
maybe_build_stream_local_openai_cli_decision_payload,
maybe_build_sync_local_openai_cli_decision_payload,
};
pub(crate) use crate::gateway::ai_pipeline::conversion::request::{
convert_openai_chat_request_to_claude_request, convert_openai_chat_request_to_gemini_request,
convert_openai_chat_request_to_openai_cli_request, extract_openai_text_content,
normalize_openai_cli_request_to_openai_chat_request, parse_openai_tool_result_content,
};
pub(crate) use crate::gateway::ai_pipeline::conversion::{
build_core_error_body_for_client_format, request_conversion_kind,
request_conversion_transport_supported, sync_chat_response_conversion_kind,
sync_cli_response_conversion_kind, RequestConversionKind, SyncChatResponseConversionKind,
SyncCliResponseConversionKind,
};
pub(crate) use self::matrix::{
build_standard_request_body, build_standard_upstream_url,
normalize_standard_request_to_openai_chat_request,
};
pub(crate) use self::openai::{
copy_request_number_field, copy_request_number_field_as,
map_openai_reasoning_effort_to_claude_output, map_openai_reasoning_effort_to_gemini_budget,
maybe_build_stream_local_decision_payload, maybe_build_sync_local_decision_payload,
maybe_execute_stream_via_local_decision, maybe_execute_sync_via_local_decision,
parse_openai_stop_sequences, resolve_openai_chat_max_tokens, value_as_u64,
};
pub(crate) use self::openai::{
maybe_build_stream_local_openai_cli_decision_payload,
maybe_build_sync_local_openai_cli_decision_payload,
maybe_execute_stream_via_local_openai_cli_decision,
maybe_execute_sync_via_local_openai_cli_decision,
};
pub(crate) async fn maybe_execute_sync_via_local_standard_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> {
if let Some(response) = self::claude::maybe_execute_sync_via_local_claude_decision(
state, parts, trace_id, decision, body_json, plan_kind,
)
.await?
{
return Ok(Some(response));
}
self::gemini::maybe_execute_sync_via_local_gemini_decision(
state, parts, trace_id, decision, body_json, plan_kind,
)
.await
}
pub(crate) async fn maybe_execute_stream_via_local_standard_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> {
if let Some(response) = self::claude::maybe_execute_stream_via_local_claude_decision(
state, parts, trace_id, decision, body_json, plan_kind,
)
.await?
{
return Ok(Some(response));
}
self::gemini::maybe_execute_stream_via_local_gemini_decision(
state, parts, trace_id, decision, body_json, plan_kind,
)
.await
}
pub(crate) async fn maybe_build_sync_local_standard_decision_payload(
state: &AppState,

View File

@@ -11,10 +11,139 @@ use crate::gateway::ai_pipeline::conversion::request::{
use crate::gateway::ai_pipeline::conversion::{request_conversion_kind, RequestConversionKind};
use crate::gateway::provider_transport::{
apply_local_body_rules, build_antigravity_v1internal_url, build_claude_messages_url,
build_gemini_content_url, build_openai_cli_url, build_passthrough_path_url,
AntigravityRequestUrlAction,
build_gemini_content_url, build_openai_chat_url, build_openai_cli_url,
build_passthrough_path_url, AntigravityRequestUrlAction,
};
pub(crate) fn build_local_openai_chat_request_body(
body_json: &Value,
mapped_model: &str,
upstream_is_stream: bool,
body_rules: Option<&Value>,
) -> Option<Value> {
let request_body_object = body_json.as_object()?;
let mut provider_request_body = serde_json::Map::from_iter(
request_body_object
.iter()
.map(|(key, value)| (key.clone(), value.clone())),
);
provider_request_body.insert("model".to_string(), Value::String(mapped_model.to_string()));
if upstream_is_stream {
provider_request_body.insert("stream".to_string(), Value::Bool(true));
}
let mut provider_request_body = Value::Object(provider_request_body);
if !apply_local_body_rules(&mut provider_request_body, body_rules, Some(body_json)) {
return None;
}
Some(provider_request_body)
}
pub(crate) fn build_local_openai_chat_upstream_url(
parts: &http::request::Parts,
transport: &crate::gateway::provider_transport::GatewayProviderTransportSnapshot,
) -> Option<String> {
let custom_path = transport
.endpoint
.custom_path
.as_deref()
.map(str::trim)
.filter(|value| !value.is_empty());
match custom_path {
Some(path) => {
build_passthrough_path_url(&transport.endpoint.base_url, path, parts.uri.query(), &[])
}
None => Some(build_openai_chat_url(
&transport.endpoint.base_url,
parts.uri.query(),
)),
}
}
pub(crate) fn build_cross_format_openai_chat_request_body(
body_json: &Value,
mapped_model: &str,
provider_api_format: &str,
upstream_is_stream: bool,
body_rules: Option<&Value>,
) -> Option<Value> {
let conversion_kind = request_conversion_kind("openai:chat", provider_api_format)?;
let mut provider_request_body = match conversion_kind {
RequestConversionKind::ToClaudeStandard => convert_openai_chat_request_to_claude_request(
body_json,
mapped_model,
upstream_is_stream,
)?,
RequestConversionKind::ToGeminiStandard => convert_openai_chat_request_to_gemini_request(
body_json,
mapped_model,
upstream_is_stream,
)?,
RequestConversionKind::ToOpenAIFamilyCli => {
convert_openai_chat_request_to_openai_cli_request(
body_json,
mapped_model,
upstream_is_stream,
false,
)?
}
RequestConversionKind::ToOpenAICompact => {
convert_openai_chat_request_to_openai_cli_request(body_json, mapped_model, false, true)?
}
_ => return None,
};
if !apply_local_body_rules(&mut provider_request_body, body_rules, Some(body_json)) {
return None;
}
Some(provider_request_body)
}
pub(crate) fn build_cross_format_openai_chat_upstream_url(
parts: &http::request::Parts,
transport: &crate::gateway::provider_transport::GatewayProviderTransportSnapshot,
mapped_model: &str,
provider_api_format: &str,
upstream_is_stream: bool,
) -> Option<String> {
let conversion_kind = request_conversion_kind("openai:chat", provider_api_format)?;
let custom_path = transport
.endpoint
.custom_path
.as_deref()
.map(str::trim)
.filter(|value| !value.is_empty());
match custom_path {
Some(path) => {
build_passthrough_path_url(&transport.endpoint.base_url, path, parts.uri.query(), &[])
}
None => match conversion_kind {
RequestConversionKind::ToClaudeStandard => Some(build_claude_messages_url(
&transport.endpoint.base_url,
parts.uri.query(),
)),
RequestConversionKind::ToGeminiStandard => build_gemini_content_url(
&transport.endpoint.base_url,
mapped_model,
upstream_is_stream,
parts.uri.query(),
),
RequestConversionKind::ToOpenAIFamilyCli => Some(build_openai_cli_url(
&transport.endpoint.base_url,
parts.uri.query(),
false,
)),
RequestConversionKind::ToOpenAICompact => Some(build_openai_cli_url(
&transport.endpoint.base_url,
parts.uri.query(),
true,
)),
_ => None,
},
}
}
pub(crate) fn build_local_openai_cli_request_body(
body_json: &Value,
mapped_model: &str,

View File

@@ -8,6 +8,14 @@ use uuid::Uuid;
use crate::gateway::ai_pipeline::conversion::{
request_conversion_direct_auth, request_conversion_kind, request_conversion_transport_supported,
};
use crate::gateway::ai_pipeline::planner::plan_builders::{
LocalStreamPlanAndReport, LocalSyncPlanAndReport,
};
use crate::gateway::ai_pipeline::planner::prefer_local_tunnel_owner_candidates;
use crate::gateway::ai_pipeline::planner::{
EXECUTION_RUNTIME_STREAM_DECISION_ACTION, EXECUTION_RUNTIME_SYNC_DECISION_ACTION,
OPENAI_CHAT_STREAM_PLAN_KIND,
};
use crate::gateway::headers::collect_control_headers;
use crate::gateway::provider_transport::{
apply_local_header_rules, build_openai_passthrough_headers, ensure_upstream_auth_header,
@@ -15,23 +23,16 @@ use crate::gateway::provider_transport::{
resolve_transport_proxy_snapshot_with_tunnel_affinity, resolve_transport_tls_profile,
supports_local_openai_chat_transport, LocalResolvedOAuthRequestAuth,
};
use crate::gateway::request_candidates::record_local_request_candidate_status;
use crate::gateway::ai_pipeline::planner::plan_builders::{
LocalStreamPlanAndReport, LocalSyncPlanAndReport,
use crate::gateway::scheduler::{
record_local_request_candidate_status, GatewayMinimalCandidateSelectionCandidate,
};
use crate::gateway::ai_pipeline::planner::prefer_local_tunnel_owner_candidates;
use crate::gateway::scheduler::GatewayMinimalCandidateSelectionCandidate;
use crate::gateway::{
append_execution_contract_fields_to_value, AppState, ConversionMode, ExecutionStrategy,
GatewayControlSyncDecisionResponse,
};
use crate::gateway::ai_pipeline::planner::{
EXECUTION_RUNTIME_STREAM_DECISION_ACTION, EXECUTION_RUNTIME_SYNC_DECISION_ACTION,
OPENAI_CHAT_STREAM_PLAN_KIND,
};
use super::plans::current_unix_secs;
use crate::gateway::ai_pipeline::planner::standard::openai::{
use crate::gateway::ai_pipeline::planner::standard::{
build_cross_format_openai_chat_request_body, build_cross_format_openai_chat_upstream_url,
build_local_openai_chat_request_body, build_local_openai_chat_upstream_url,
};

View File

@@ -13,12 +13,12 @@ use crate::gateway::ai_pipeline::planner::plan_builders::{
LocalStreamPlanAndReport, LocalSyncPlanAndReport,
};
use crate::gateway::ai_pipeline::planner::prefer_local_tunnel_owner_candidates;
use crate::gateway::scheduler::{
list_selectable_candidates, GatewayMinimalCandidateSelectionCandidate,
};
use crate::gateway::ai_pipeline::planner::{
OPENAI_CHAT_STREAM_PLAN_KIND, OPENAI_CHAT_SYNC_PLAN_KIND,
};
use crate::gateway::scheduler::{
list_selectable_candidates, GatewayMinimalCandidateSelectionCandidate,
};
pub(super) fn build_local_openai_chat_miss_diagnostic(
decision: &GatewayControlDecision,

View File

@@ -5,12 +5,19 @@ use serde_json::{json, Value};
use tracing::warn;
use uuid::Uuid;
use crate::gateway::ai_pipeline::planner::standard::openai::{
use crate::gateway::ai_pipeline::conversion::{
request_conversion_direct_auth, request_conversion_kind, request_conversion_transport_supported,
};
use crate::gateway::ai_pipeline::planner::plan_builders::{
LocalStreamPlanAndReport, LocalSyncPlanAndReport,
};
use crate::gateway::ai_pipeline::planner::prefer_local_tunnel_owner_candidates;
use crate::gateway::ai_pipeline::planner::standard::{
build_cross_format_openai_cli_request_body, build_cross_format_openai_cli_upstream_url,
build_local_openai_cli_request_body, build_local_openai_cli_upstream_url,
};
use crate::gateway::ai_pipeline::conversion::{
request_conversion_direct_auth, request_conversion_kind, request_conversion_transport_supported,
use crate::gateway::ai_pipeline::planner::{
EXECUTION_RUNTIME_STREAM_DECISION_ACTION, EXECUTION_RUNTIME_SYNC_DECISION_ACTION,
};
use crate::gateway::headers::collect_control_headers;
use crate::gateway::provider_transport::{
@@ -23,23 +30,14 @@ use crate::gateway::provider_transport::{
AntigravityRequestEnvelopeSupport, AntigravityRequestSideSupport,
LocalResolvedOAuthRequestAuth,
};
use crate::gateway::request_candidates::{
current_unix_secs, record_local_request_candidate_status,
};
use crate::gateway::ai_pipeline::planner::plan_builders::{
LocalStreamPlanAndReport, LocalSyncPlanAndReport,
};
use crate::gateway::ai_pipeline::planner::prefer_local_tunnel_owner_candidates;
use crate::gateway::scheduler::{
list_selectable_candidates, GatewayMinimalCandidateSelectionCandidate,
current_unix_secs, list_selectable_candidates, record_local_request_candidate_status,
GatewayMinimalCandidateSelectionCandidate,
};
use crate::gateway::{
append_execution_contract_fields_to_value, AppState, ConversionMode, ExecutionStrategy,
GatewayControlDecision, GatewayControlSyncDecisionResponse, GatewayError,
};
use crate::gateway::ai_pipeline::planner::{
EXECUTION_RUNTIME_STREAM_DECISION_ACTION, EXECUTION_RUNTIME_SYNC_DECISION_ACTION,
};
const ANTIGRAVITY_ENVELOPE_NAME: &str = "antigravity:v1internal";

View File

@@ -9,11 +9,11 @@ use crate::gateway::ai_pipeline::planner::plan_builders::{
build_openai_cli_stream_plan_from_decision, build_openai_cli_sync_plan_from_decision,
LocalStreamPlanAndReport, LocalSyncPlanAndReport,
};
use crate::gateway::{AppState, GatewayControlDecision, GatewayError};
use crate::gateway::ai_pipeline::planner::{
OPENAI_CLI_STREAM_PLAN_KIND, OPENAI_CLI_SYNC_PLAN_KIND, OPENAI_COMPACT_STREAM_PLAN_KIND,
OPENAI_COMPACT_SYNC_PLAN_KIND,
};
use crate::gateway::{AppState, GatewayControlDecision, GatewayError};
pub(super) fn resolve_sync_spec(plan_kind: &str) -> Option<LocalOpenAiCliSpec> {
match plan_kind {

View File

@@ -1,31 +0,0 @@
mod chat;
mod cli;
mod normalize_chat;
mod normalize_cli;
pub(crate) use crate::gateway::ai_pipeline::conversion::request::{
convert_openai_chat_request_to_claude_request, convert_openai_chat_request_to_gemini_request,
convert_openai_chat_request_to_openai_cli_request, extract_openai_text_content,
normalize_openai_cli_request_to_openai_chat_request, parse_openai_tool_result_content,
};
pub(crate) use self::chat::{
copy_request_number_field, copy_request_number_field_as,
map_openai_reasoning_effort_to_claude_output, map_openai_reasoning_effort_to_gemini_budget,
maybe_build_stream_local_decision_payload, maybe_build_sync_local_decision_payload,
maybe_execute_stream_via_local_decision, maybe_execute_sync_via_local_decision,
parse_openai_stop_sequences, resolve_openai_chat_max_tokens, value_as_u64,
};
pub(crate) use self::cli::{
maybe_build_stream_local_openai_cli_decision_payload,
maybe_build_sync_local_openai_cli_decision_payload,
maybe_execute_stream_via_local_openai_cli_decision,
maybe_execute_sync_via_local_openai_cli_decision,
};
pub(crate) use self::normalize_chat::{
build_cross_format_openai_chat_request_body, build_cross_format_openai_chat_upstream_url,
build_local_openai_chat_request_body, build_local_openai_chat_upstream_url,
};
pub(crate) use self::normalize_cli::{
build_cross_format_openai_cli_request_body, build_cross_format_openai_cli_upstream_url,
build_local_openai_cli_request_body, build_local_openai_cli_upstream_url,
};

View File

@@ -1,5 +0,0 @@
mod request;
pub(crate) use self::request::{
build_cross_format_openai_chat_request_body, build_cross_format_openai_chat_upstream_url,
build_local_openai_chat_request_body, build_local_openai_chat_upstream_url,
};

View File

@@ -1,143 +0,0 @@
use serde_json::Value;
use crate::gateway::ai_pipeline::conversion::request::{
convert_openai_chat_request_to_claude_request, convert_openai_chat_request_to_gemini_request,
convert_openai_chat_request_to_openai_cli_request,
};
use crate::gateway::ai_pipeline::conversion::{request_conversion_kind, RequestConversionKind};
use crate::gateway::provider_transport::{
apply_local_body_rules, build_claude_messages_url, build_gemini_content_url,
build_openai_chat_url, build_openai_cli_url, build_passthrough_path_url,
};
pub(crate) fn build_local_openai_chat_request_body(
body_json: &serde_json::Value,
mapped_model: &str,
upstream_is_stream: bool,
body_rules: Option<&serde_json::Value>,
) -> Option<serde_json::Value> {
let request_body_object = body_json.as_object()?;
let mut provider_request_body = serde_json::Map::from_iter(
request_body_object
.iter()
.map(|(key, value)| (key.clone(), value.clone())),
);
provider_request_body.insert(
"model".to_string(),
serde_json::Value::String(mapped_model.to_string()),
);
if upstream_is_stream {
provider_request_body.insert("stream".to_string(), serde_json::Value::Bool(true));
}
let mut provider_request_body = serde_json::Value::Object(provider_request_body);
if !apply_local_body_rules(&mut provider_request_body, body_rules, Some(body_json)) {
return None;
}
Some(provider_request_body)
}
pub(crate) fn build_local_openai_chat_upstream_url(
parts: &http::request::Parts,
transport: &crate::gateway::provider_transport::GatewayProviderTransportSnapshot,
) -> Option<String> {
let custom_path = transport
.endpoint
.custom_path
.as_deref()
.map(str::trim)
.filter(|value| !value.is_empty());
match custom_path {
Some(path) => {
build_passthrough_path_url(&transport.endpoint.base_url, path, parts.uri.query(), &[])
}
None => Some(build_openai_chat_url(
&transport.endpoint.base_url,
parts.uri.query(),
)),
}
}
pub(crate) fn build_cross_format_openai_chat_request_body(
body_json: &Value,
mapped_model: &str,
provider_api_format: &str,
upstream_is_stream: bool,
body_rules: Option<&Value>,
) -> Option<Value> {
let conversion_kind = request_conversion_kind("openai:chat", provider_api_format)?;
let mut provider_request_body = match conversion_kind {
RequestConversionKind::ToClaudeStandard => convert_openai_chat_request_to_claude_request(
body_json,
mapped_model,
upstream_is_stream,
)?,
RequestConversionKind::ToGeminiStandard => convert_openai_chat_request_to_gemini_request(
body_json,
mapped_model,
upstream_is_stream,
)?,
RequestConversionKind::ToOpenAIFamilyCli => {
convert_openai_chat_request_to_openai_cli_request(
body_json,
mapped_model,
upstream_is_stream,
false,
)?
}
RequestConversionKind::ToOpenAICompact => {
convert_openai_chat_request_to_openai_cli_request(body_json, mapped_model, false, true)?
}
_ => return None,
};
if !apply_local_body_rules(&mut provider_request_body, body_rules, Some(body_json)) {
return None;
}
Some(provider_request_body)
}
pub(crate) fn build_cross_format_openai_chat_upstream_url(
parts: &http::request::Parts,
transport: &crate::gateway::provider_transport::GatewayProviderTransportSnapshot,
mapped_model: &str,
provider_api_format: &str,
upstream_is_stream: bool,
) -> Option<String> {
let conversion_kind = request_conversion_kind("openai:chat", provider_api_format)?;
let custom_path = transport
.endpoint
.custom_path
.as_deref()
.map(str::trim)
.filter(|value| !value.is_empty());
match custom_path {
Some(path) => {
build_passthrough_path_url(&transport.endpoint.base_url, path, parts.uri.query(), &[])
}
None => match conversion_kind {
RequestConversionKind::ToClaudeStandard => Some(build_claude_messages_url(
&transport.endpoint.base_url,
parts.uri.query(),
)),
RequestConversionKind::ToGeminiStandard => build_gemini_content_url(
&transport.endpoint.base_url,
mapped_model,
upstream_is_stream,
parts.uri.query(),
),
RequestConversionKind::ToOpenAIFamilyCli => Some(build_openai_cli_url(
&transport.endpoint.base_url,
parts.uri.query(),
false,
)),
RequestConversionKind::ToOpenAICompact => Some(build_openai_cli_url(
&transport.endpoint.base_url,
parts.uri.query(),
true,
)),
_ => None,
},
}
}

View File

@@ -5,7 +5,7 @@ use super::{
GatewayControlSyncDecisionResponse, GatewayError, LocalStreamPlanAndReport,
LocalSyncPlanAndReport,
};
use crate::gateway::ai_pipeline::private_surfaces::provider_adaptation_requires_eventstream_accept;
use crate::gateway::ai_pipeline::adaptation::surfaces::provider_adaptation_requires_eventstream_accept;
use crate::gateway::provider_transport::{
build_openai_chat_url, build_openai_cli_url, build_openai_passthrough_headers,
ensure_upstream_auth_header,

View File

@@ -1,29 +1,27 @@
use axum::body::Body;
use axum::http::Response;
use serde_json::{Map, Value};
use tracing::warn;
use crate::gateway::{
execute_execution_runtime_stream, execute_execution_runtime_sync, AppState,
GatewayControlDecision, GatewayControlSyncDecisionResponse, GatewayError,
LocalExecutionRuntimeMissDiagnostic,
};
use crate::gateway::ai_pipeline::planner::{
OPENAI_CHAT_STREAM_PLAN_KIND, OPENAI_CHAT_SYNC_PLAN_KIND,
};
use crate::gateway::{
AppState, GatewayControlDecision, GatewayControlSyncDecisionResponse, GatewayError,
LocalExecutionRuntimeMissDiagnostic,
};
use crate::gateway::ai_pipeline::planner::standard::openai::{
use crate::gateway::ai_pipeline::planner::standard::{
convert_openai_chat_request_to_claude_request, convert_openai_chat_request_to_gemini_request,
convert_openai_chat_request_to_openai_cli_request, extract_openai_text_content,
parse_openai_tool_result_content,
};
#[path = "openai/chat/decision.rs"]
mod decision;
#[path = "openai/chat/plans.rs"]
mod plans;
use self::decision::{
mark_unused_local_openai_chat_candidates, materialize_local_openai_chat_candidate_attempts,
materialize_local_openai_chat_candidate_attempts,
maybe_build_local_openai_chat_decision_payload_for_candidate, LocalOpenAiChatDecisionInput,
};
use self::plans::{
@@ -33,93 +31,48 @@ use self::plans::{
set_local_openai_chat_miss_diagnostic,
};
pub(crate) async fn maybe_execute_sync_via_local_decision(
pub(crate) async fn build_local_openai_chat_sync_plan_and_reports_for_kind(
state: &AppState,
parts: &http::request::Parts,
trace_id: &str,
decision: &GatewayControlDecision,
body_json: &serde_json::Value,
plan_kind: &str,
) -> Result<Option<Response<Body>>, GatewayError> {
let plan_and_reports = build_local_openai_chat_sync_plan_and_reports(
) -> Result<
Vec<crate::gateway::ai_pipeline::planner::plan_builders::LocalSyncPlanAndReport>,
GatewayError,
> {
build_local_openai_chat_sync_plan_and_reports(
state, parts, trace_id, decision, body_json, plan_kind,
)
.await?;
if plan_and_reports.is_empty() {
return Ok(None);
}
let plan_count = plan_and_reports.len();
let mut remaining = plan_and_reports.into_iter();
while let Some(plan_and_report) = remaining.next() {
if let Some(response) = execute_execution_runtime_sync(
state,
parts.uri.path(),
plan_and_report.plan,
trace_id,
decision,
plan_kind,
plan_and_report.report_kind,
plan_and_report.report_context,
)
.await?
{
mark_unused_local_openai_chat_candidates(state, remaining.collect()).await;
return Ok(Some(response));
}
}
state.set_local_execution_runtime_miss_diagnostic(
trace_id,
LocalExecutionRuntimeMissDiagnostic {
candidate_count: Some(plan_count),
..build_local_openai_chat_miss_diagnostic(
decision,
plan_kind,
body_json.get("model").and_then(|value| value.as_str()),
"execution_runtime_candidates_exhausted",
)
},
);
Ok(None)
.await
}
pub(crate) async fn maybe_execute_stream_via_local_decision(
pub(crate) async fn build_local_openai_chat_stream_plan_and_reports_for_kind(
state: &AppState,
parts: &http::request::Parts,
trace_id: &str,
decision: &GatewayControlDecision,
body_json: &serde_json::Value,
plan_kind: &str,
) -> Result<Option<Response<Body>>, GatewayError> {
let plan_and_reports = build_local_openai_chat_stream_plan_and_reports(
) -> Result<
Vec<crate::gateway::ai_pipeline::planner::plan_builders::LocalStreamPlanAndReport>,
GatewayError,
> {
build_local_openai_chat_stream_plan_and_reports(
state, parts, trace_id, decision, body_json, plan_kind,
)
.await?;
if plan_and_reports.is_empty() {
return Ok(None);
}
let plan_count = plan_and_reports.len();
let mut remaining = plan_and_reports.into_iter();
while let Some(plan_and_report) = remaining.next() {
if let Some(response) = execute_execution_runtime_stream(
state,
plan_and_report.plan,
trace_id,
decision,
plan_kind,
plan_and_report.report_kind,
plan_and_report.report_context,
)
.await?
{
mark_unused_local_openai_chat_candidates(state, remaining.collect()).await;
return Ok(Some(response));
}
}
.await
}
pub(crate) fn set_local_openai_chat_execution_exhausted_diagnostic(
state: &AppState,
trace_id: &str,
decision: &GatewayControlDecision,
plan_kind: &str,
body_json: &serde_json::Value,
plan_count: usize,
) {
state.set_local_execution_runtime_miss_diagnostic(
trace_id,
LocalExecutionRuntimeMissDiagnostic {
@@ -132,8 +85,6 @@ pub(crate) async fn maybe_execute_stream_via_local_decision(
)
},
);
Ok(None)
}
pub(crate) async fn maybe_build_sync_local_decision_payload(

View File

@@ -1,15 +1,17 @@
use crate::gateway::{
execute_execution_runtime_stream, execute_execution_runtime_sync, AppState,
GatewayControlDecision, GatewayControlSyncDecisionResponse, GatewayError,
use crate::gateway::ai_pipeline::planner::plan_builders::{
LocalStreamPlanAndReport, LocalSyncPlanAndReport,
};
use crate::gateway::{
AppState, GatewayControlDecision, GatewayControlSyncDecisionResponse, GatewayError,
};
use axum::body::Body;
use axum::http::Response;
#[path = "openai/cli/decision.rs"]
mod decision;
#[path = "openai/cli/plans.rs"]
mod plans;
use self::decision::{
mark_unused_local_openai_cli_candidates, materialize_local_openai_cli_candidate_attempts,
materialize_local_openai_cli_candidate_attempts,
maybe_build_local_openai_cli_decision_payload_for_candidate,
resolve_local_openai_cli_decision_input,
};
@@ -18,85 +20,34 @@ use self::plans::{
resolve_sync_spec,
};
pub(crate) async fn maybe_execute_sync_via_local_openai_cli_decision(
pub(crate) async fn build_local_openai_cli_sync_plan_and_reports_for_kind(
state: &AppState,
parts: &http::request::Parts,
trace_id: &str,
decision: &GatewayControlDecision,
body_json: &serde_json::Value,
plan_kind: &str,
) -> Result<Option<Response<Body>>, GatewayError> {
) -> Result<Vec<LocalSyncPlanAndReport>, GatewayError> {
let Some(spec) = resolve_sync_spec(plan_kind) else {
return Ok(None);
return Ok(Vec::new());
};
let plan_and_reports =
build_local_sync_plan_and_reports(state, parts, trace_id, decision, body_json, spec)
.await?;
if plan_and_reports.is_empty() {
return Ok(None);
}
let mut remaining = plan_and_reports.into_iter();
while let Some(plan_and_report) = remaining.next() {
if let Some(response) = execute_execution_runtime_sync(
state,
parts.uri.path(),
plan_and_report.plan,
trace_id,
decision,
plan_kind,
plan_and_report.report_kind,
plan_and_report.report_context,
)
.await?
{
mark_unused_local_openai_cli_candidates(state, remaining.collect()).await;
return Ok(Some(response));
}
}
Ok(None)
build_local_sync_plan_and_reports(state, parts, trace_id, decision, body_json, spec).await
}
pub(crate) async fn maybe_execute_stream_via_local_openai_cli_decision(
pub(crate) async fn build_local_openai_cli_stream_plan_and_reports_for_kind(
state: &AppState,
parts: &http::request::Parts,
trace_id: &str,
decision: &GatewayControlDecision,
body_json: &serde_json::Value,
plan_kind: &str,
) -> Result<Option<Response<Body>>, GatewayError> {
) -> Result<Vec<LocalStreamPlanAndReport>, GatewayError> {
let Some(spec) = resolve_stream_spec(plan_kind) else {
return Ok(None);
return Ok(Vec::new());
};
let plan_and_reports =
build_local_stream_plan_and_reports(state, parts, trace_id, decision, body_json, spec)
.await?;
if plan_and_reports.is_empty() {
return Ok(None);
}
let mut remaining = plan_and_reports.into_iter();
while let Some(plan_and_report) = remaining.next() {
if let Some(response) = execute_execution_runtime_stream(
state,
plan_and_report.plan,
trace_id,
decision,
plan_kind,
plan_and_report.report_kind,
plan_and_report.report_context,
)
.await?
{
mark_unused_local_openai_cli_candidates(state, remaining.collect()).await;
return Ok(Some(response));
}
}
Ok(None)
build_local_stream_plan_and_reports(state, parts, trace_id, decision, body_json, spec).await
}
pub(crate) async fn maybe_build_sync_local_openai_cli_decision_payload(

View File

@@ -2,7 +2,7 @@ use aether_contracts::RequestBody;
use super::augment_sync_report_context;
use super::*;
use crate::gateway::ai_pipeline::private_surfaces::provider_adaptation_requires_eventstream_accept;
use crate::gateway::ai_pipeline::adaptation::surfaces::provider_adaptation_requires_eventstream_accept;
use crate::gateway::provider_transport::ensure_upstream_auth_header;
pub(crate) fn build_standard_sync_plan_from_decision(

View File

@@ -7,6 +7,7 @@ mod headers;
mod policy;
mod refresh;
mod request;
#[path = "../../../adaptation/kiro_stream.rs"]
mod stream;
mod url;

View File

@@ -36,6 +36,12 @@ pub(crate) use super::super::execution_runtime::{
execute_execution_runtime_stream, execute_execution_runtime_sync,
execute_execution_runtime_sync_plan,
};
pub(crate) use super::adaptation::private_envelope::{
maybe_build_provider_private_stream_normalizer,
maybe_normalize_provider_private_sync_report_payload,
normalize_provider_private_report_context, normalize_provider_private_response_value,
provider_private_response_allows_sync_finalize, transform_provider_private_stream_line,
};
pub(crate) use adapters::antigravity::{
build_antigravity_safe_v1internal_request, build_antigravity_static_identity_headers,
build_antigravity_v1internal_url, classify_local_antigravity_request_support,
@@ -67,12 +73,6 @@ pub(crate) use adapters::openai::{
build_openai_chat_url, build_openai_cli_url, build_openai_passthrough_headers,
resolve_local_openai_chat_auth, supports_local_openai_chat_transport,
};
pub(crate) use super::private_response::{
maybe_build_provider_private_stream_normalizer,
maybe_normalize_provider_private_sync_report_payload,
normalize_provider_private_report_context, normalize_provider_private_response_value,
provider_private_response_allows_sync_finalize, transform_provider_private_stream_line,
};
pub(crate) use adapters::vertex::{
build_vertex_api_key_gemini_content_url, resolve_local_vertex_api_key_query_auth,
supports_local_vertex_api_key_gemini_transport_with_network,

View File

@@ -2,6 +2,7 @@ pub(crate) mod ai;
pub(crate) mod backend;
mod core;
mod ops;
pub(crate) mod response;
pub(crate) use ai::mount_ai_routes;
pub(crate) use backend::{

View File

@@ -24,7 +24,7 @@ pub(crate) async fn maybe_execute_via_execution_runtime_stream(
if parts.method != http::Method::POST {
return Ok(None);
}
return crate::gateway::ai_pipeline::planner::maybe_execute_stream_local_path(
return crate::gateway::executor::maybe_execute_stream_local_path(
state, parts, body_bytes, trace_id, decision,
)
.await;
@@ -39,7 +39,7 @@ pub(crate) async fn maybe_execute_via_execution_runtime_stream(
{
return Ok(None);
}
crate::gateway::ai_pipeline::planner::maybe_execute_stream_local_path(
crate::gateway::executor::maybe_execute_stream_local_path(
state, parts, body_bytes, trace_id, decision,
)
.await

View File

@@ -9,9 +9,10 @@ use serde_json::json;
use tokio_util::codec::{FramedRead, LinesCodec};
use tracing::warn;
use crate::gateway::api::response::build_client_response_from_parts;
use crate::gateway::execution_runtime::ndjson::decode_stream_frame_ndjson;
use crate::gateway::execution_runtime::submission::{has_nested_error, strip_utf8_bom_and_ws};
use crate::gateway::{build_client_response_from_parts, GatewayControlDecision, GatewayError};
use crate::gateway::{GatewayControlDecision, GatewayError};
use crate::gateway::{
GEMINI_FILES_DOWNLOAD_PLAN_KIND, MAX_ERROR_BODY_BYTES, MAX_STREAM_PREFETCH_FRAMES,
OPENAI_VIDEO_CONTENT_PLAN_KIND,

View File

@@ -26,6 +26,9 @@ use self::execution_failures::{
use crate::gateway::ai_pipeline::runtime::{
maybe_build_provider_private_stream_normalizer, normalize_provider_private_report_context,
};
use crate::gateway::api::response::{
attach_control_metadata_headers, build_client_response, build_client_response_from_parts,
};
use crate::gateway::constants::{CONTROL_CANDIDATE_ID_HEADER, CONTROL_REQUEST_ID_HEADER};
use crate::gateway::execution_runtime::build_direct_execution_frame_stream;
#[cfg(test)]
@@ -36,18 +39,15 @@ use crate::gateway::execution_runtime::submission::{
use crate::gateway::execution_runtime::transport::{
DirectSyncExecutionRuntime, DirectUpstreamStreamExecution,
};
use crate::gateway::request_candidates::{
use crate::gateway::scheduler::{
current_unix_secs as current_request_candidate_unix_secs,
ensure_execution_request_candidate_slot, record_local_request_candidate_status,
};
use crate::gateway::scheduler::{
resolve_core_stream_direct_finalize_report_kind,
resolve_core_stream_error_finalize_report_kind, should_fallback_to_control_stream,
should_retry_next_local_candidate_stream,
};
use crate::gateway::usage::submit_stream_report;
use crate::gateway::{
attach_control_metadata_headers, build_client_response, build_client_response_from_parts,
maybe_build_stream_response_rewriter, AppState, GatewayControlDecision, GatewayError,
GatewayStreamReportRequest, GatewaySyncReportRequest, MAX_STREAM_PREFETCH_BYTES,
MAX_STREAM_PREFETCH_FRAMES,

View File

@@ -5,18 +5,16 @@ use base64::Engine as _;
use serde_json::{Map, Value};
use tracing::warn;
use crate::gateway::api::response::attach_control_metadata_headers;
use crate::gateway::execution_runtime::submission::{
resolve_core_error_background_report_kind, submit_local_core_error_or_sync_finalize,
};
use crate::gateway::request_candidates::{
use crate::gateway::scheduler::{
current_unix_secs as current_request_candidate_unix_secs,
record_report_request_candidate_status,
};
use crate::gateway::usage::submit_sync_report;
use crate::gateway::{
attach_control_metadata_headers, AppState, GatewayControlDecision, GatewayError,
GatewaySyncReportRequest,
};
use crate::gateway::{AppState, GatewayControlDecision, GatewayError, GatewaySyncReportRequest};
#[derive(Debug, Clone)]
pub(super) struct StreamFailureReport {

View File

@@ -1,12 +1,13 @@
use crate::gateway::ai_pipeline::adaptation::private_envelope::normalize_provider_private_response_value as unwrap_local_finalize_response_value;
use crate::gateway::ai_pipeline::conversion::{
build_core_error_body_for_client_format, core_error_background_report_kind,
core_error_default_client_api_format, is_core_error_finalize_kind, LocalCoreSyncErrorKind,
};
use crate::gateway::ai_pipeline::private_response::normalize_provider_private_response_value as unwrap_local_finalize_response_value;
use crate::gateway::api::response::build_client_response_from_parts;
use crate::gateway::usage::spawn_sync_report;
use crate::gateway::{
build_client_response_from_parts, maybe_compile_sync_finalize_response, AppState,
GatewayControlDecision, GatewayError, GatewaySyncReportRequest,
maybe_compile_sync_finalize_response, AppState, GatewayControlDecision, GatewayError,
GatewaySyncReportRequest,
};
use axum::body::Body;
use axum::http::Response;

View File

@@ -30,7 +30,7 @@ pub(crate) async fn maybe_execute_via_execution_runtime_sync(
if parts.method != http::Method::POST {
return Ok(None);
}
return crate::gateway::ai_pipeline::planner::maybe_execute_sync_local_path(
return crate::gateway::executor::maybe_execute_sync_local_path(
state, parts, body_bytes, trace_id, decision,
)
.await;
@@ -45,7 +45,7 @@ pub(crate) async fn maybe_execute_via_execution_runtime_sync(
{
return Ok(None);
}
crate::gateway::ai_pipeline::planner::maybe_execute_sync_local_path(
crate::gateway::executor::maybe_execute_sync_local_path(
state, parts, body_bytes, trace_id, decision,
)
.await

View File

@@ -6,24 +6,25 @@ use axum::http::Response;
use base64::Engine as _;
use tracing::warn;
use crate::gateway::ai_pipeline::contracts::implicit_sync_finalize_report_kind;
use crate::gateway::api::response::{
attach_control_metadata_headers, build_client_response, build_client_response_from_parts,
};
use crate::gateway::constants::{CONTROL_CANDIDATE_ID_HEADER, CONTROL_REQUEST_ID_HEADER};
#[cfg(test)]
use crate::gateway::execution_runtime::remote_compat::post_sync_plan_to_remote_execution_runtime;
use crate::gateway::execution_runtime::submission::submit_local_core_error_or_sync_finalize;
use crate::gateway::execution_runtime::transport::DirectSyncExecutionRuntime;
use crate::gateway::request_candidates::{
use crate::gateway::scheduler::{
current_unix_secs as current_request_candidate_unix_secs,
ensure_execution_request_candidate_slot, execution_error_details,
record_local_request_candidate_status,
};
use crate::gateway::scheduler::{
resolve_core_sync_error_finalize_report_kind, should_fallback_to_control_sync,
should_finalize_sync_response, should_retry_next_local_candidate_sync,
record_local_request_candidate_status, resolve_core_sync_error_finalize_report_kind,
should_fallback_to_control_sync, should_finalize_sync_response,
should_retry_next_local_candidate_sync,
};
use crate::gateway::usage::{spawn_sync_report, submit_sync_report};
use crate::gateway::video_tasks::VideoTaskSyncReportMode;
use crate::gateway::{
attach_control_metadata_headers, build_client_response, build_client_response_from_parts,
maybe_build_sync_finalize_outcome, AppState, GatewayControlDecision, GatewayError,
GatewaySyncReportRequest,
};
@@ -498,19 +499,6 @@ pub(crate) async fn execute_execution_runtime_sync(
)?))
}
fn resolve_implicit_sync_finalize_report_kind(plan_kind: &str) -> Option<&'static str> {
match plan_kind {
"openai_chat_sync" => Some("openai_chat_sync_finalize"),
"claude_chat_sync" => Some("claude_chat_sync_finalize"),
"gemini_chat_sync" => Some("gemini_chat_sync_finalize"),
"openai_cli_sync" => Some("openai_cli_sync_finalize"),
"openai_compact_sync" => Some("openai_compact_sync_finalize"),
"claude_cli_sync" => Some("claude_cli_sync_finalize"),
"gemini_cli_sync" => Some("gemini_cli_sync_finalize"),
_ => None,
}
}
#[allow(clippy::too_many_arguments)] // mirrors sync execution context
fn maybe_build_implicit_sync_finalize_outcome(
trace_id: &str,
@@ -527,7 +515,7 @@ fn maybe_build_implicit_sync_finalize_outcome(
return Ok(None);
}
let Some(report_kind) = resolve_implicit_sync_finalize_report_kind(plan_kind) else {
let Some(report_kind) = implicit_sync_finalize_report_kind(plan_kind) else {
return Ok(None);
};

View File

@@ -5,12 +5,10 @@ use axum::body::Body;
use axum::http::Response;
use serde_json::json;
use crate::gateway::api::response::build_client_response_from_parts;
use crate::gateway::video_tasks::{LocalVideoTaskSnapshot, VideoTaskSyncReportMode};
use crate::gateway::VideoTaskService;
use crate::gateway::{
build_client_response_from_parts, GatewayControlDecision, GatewayError,
GatewaySyncReportRequest,
};
use crate::gateway::{GatewayControlDecision, GatewayError, GatewaySyncReportRequest};
pub(crate) struct LocalVideoSyncSuccessOutcome {
pub(crate) response: Response<Body>,

View File

@@ -2,16 +2,16 @@ use aether_contracts::{ExecutionPlan, RequestBody};
use axum::http::Request;
use serde_json::json;
use crate::gateway::execution_runtime::submission::{
build_best_effort_local_core_error_body, resolve_core_error_background_report_kind,
resolve_core_success_background_report_kind,
};
use crate::gateway::ai_pipeline::planner::plan_builders::{
build_gemini_stream_plan_from_decision, build_gemini_sync_plan_from_decision,
build_openai_cli_stream_plan_from_decision, build_openai_cli_sync_plan_from_decision,
build_passthrough_sync_plan_from_decision, build_standard_stream_plan_from_decision,
build_standard_sync_plan_from_decision,
};
use crate::gateway::execution_runtime::submission::{
build_best_effort_local_core_error_body, resolve_core_error_background_report_kind,
resolve_core_success_background_report_kind,
};
use crate::gateway::{
resolve_local_sync_error_background_report_kind,
resolve_local_sync_success_background_report_kind, GatewayControlSyncDecisionResponse,

View File

@@ -4,7 +4,7 @@ use axum::http::Response;
use crate::gateway::ai_pipeline::planner::plan_builders::{
LocalStreamPlanAndReport, LocalSyncPlanAndReport,
};
use crate::gateway::request_candidates::record_local_request_candidate_status;
use crate::gateway::scheduler::record_local_request_candidate_status;
use crate::gateway::{
execute_execution_runtime_stream, execute_execution_runtime_sync, AppState,
GatewayControlDecision, GatewayError,

View File

@@ -0,0 +1 @@
pub(crate) use crate::gateway::LocalExecutionRuntimeMissDiagnostic;

View File

@@ -0,0 +1,7 @@
pub(crate) mod candidate_loop;
mod diagnostics;
mod orchestration;
mod reports;
mod retries;
pub(crate) use orchestration::*;

View File

@@ -0,0 +1,467 @@
use axum::body::Body;
use axum::http::Response;
use crate::gateway::ai_pipeline::planner::passthrough::provider::plans::{
build_local_stream_plan_and_reports as build_same_format_stream_plan_and_reports,
build_local_sync_plan_and_reports as build_same_format_sync_plan_and_reports,
resolve_stream_spec as resolve_same_format_stream_spec,
resolve_sync_spec as resolve_same_format_sync_spec,
};
use crate::gateway::ai_pipeline::planner::standard::family::{
build_local_stream_plan_and_reports as build_standard_stream_plan_and_reports,
build_local_sync_plan_and_reports as build_standard_sync_plan_and_reports, LocalStandardSpec,
};
use crate::gateway::ai_pipeline::planner::standard::{claude, gemini, openai_chat, openai_cli};
use crate::gateway::ai_pipeline::planner::{
parse_direct_request_body, GatewayControlSyncDecisionResponse,
EXECUTION_RUNTIME_STREAM_DECISION_ACTION, EXECUTION_RUNTIME_SYNC_DECISION_ACTION,
};
use crate::gateway::ai_pipeline::{planner, runtime};
use crate::gateway::executor::candidate_loop::{
execute_stream_plan_and_reports, execute_sync_plan_and_reports,
};
use crate::gateway::intent;
use crate::gateway::{AppState, GatewayControlDecision, GatewayError};
pub(crate) async fn maybe_execute_sync_local_path(
state: &AppState,
parts: &http::request::Parts,
body_bytes: &axum::body::Bytes,
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
}
pub(crate) async fn maybe_execute_stream_local_path(
state: &AppState,
parts: &http::request::Parts,
body_bytes: &axum::body::Bytes,
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
}
pub(crate) async fn maybe_execute_sync_via_local_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> {
let plan_and_reports = openai_chat::build_local_openai_chat_sync_plan_and_reports_for_kind(
state, parts, trace_id, decision, body_json, plan_kind,
)
.await?;
if plan_and_reports.is_empty() {
return Ok(None);
}
let plan_count = plan_and_reports.len();
if let Some(response) = execute_sync_plan_and_reports(
state,
parts,
trace_id,
decision,
plan_kind,
plan_and_reports,
)
.await?
{
return Ok(Some(response));
}
openai_chat::set_local_openai_chat_execution_exhausted_diagnostic(
state, trace_id, decision, plan_kind, body_json, plan_count,
);
Ok(None)
}
pub(crate) async fn maybe_execute_stream_via_local_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> {
let plan_and_reports = openai_chat::build_local_openai_chat_stream_plan_and_reports_for_kind(
state, parts, trace_id, decision, body_json, plan_kind,
)
.await?;
if plan_and_reports.is_empty() {
return Ok(None);
}
let plan_count = plan_and_reports.len();
if let Some(response) =
execute_stream_plan_and_reports(state, trace_id, decision, plan_kind, plan_and_reports)
.await?
{
return Ok(Some(response));
}
openai_chat::set_local_openai_chat_execution_exhausted_diagnostic(
state, trace_id, decision, plan_kind, body_json, plan_count,
);
Ok(None)
}
pub(crate) async fn maybe_execute_sync_via_local_openai_cli_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> {
let plan_and_reports = openai_cli::build_local_openai_cli_sync_plan_and_reports_for_kind(
state, parts, trace_id, decision, body_json, plan_kind,
)
.await?;
if plan_and_reports.is_empty() {
return Ok(None);
}
execute_sync_plan_and_reports(
state,
parts,
trace_id,
decision,
plan_kind,
plan_and_reports,
)
.await
}
pub(crate) async fn maybe_execute_stream_via_local_openai_cli_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> {
let plan_and_reports = openai_cli::build_local_openai_cli_stream_plan_and_reports_for_kind(
state, parts, trace_id, decision, body_json, plan_kind,
)
.await?;
if plan_and_reports.is_empty() {
return Ok(None);
}
execute_stream_plan_and_reports(state, trace_id, decision, plan_kind, plan_and_reports).await
}
pub(crate) async fn maybe_execute_sync_via_standard_family_decision(
state: &AppState,
parts: &http::request::Parts,
trace_id: &str,
decision: &GatewayControlDecision,
body_json: &serde_json::Value,
plan_kind: &str,
resolve_sync_spec: fn(&str) -> Option<LocalStandardSpec>,
) -> Result<Option<Response<Body>>, GatewayError> {
let Some(spec) = resolve_sync_spec(plan_kind) else {
return Ok(None);
};
let plan_and_reports =
build_standard_sync_plan_and_reports(state, parts, trace_id, decision, body_json, spec)
.await?;
if plan_and_reports.is_empty() {
return Ok(None);
}
execute_sync_plan_and_reports(
state,
parts,
trace_id,
decision,
plan_kind,
plan_and_reports,
)
.await
}
pub(crate) async fn maybe_execute_stream_via_standard_family_decision(
state: &AppState,
parts: &http::request::Parts,
trace_id: &str,
decision: &GatewayControlDecision,
body_json: &serde_json::Value,
plan_kind: &str,
resolve_stream_spec: fn(&str) -> Option<LocalStandardSpec>,
) -> Result<Option<Response<Body>>, GatewayError> {
let Some(spec) = resolve_stream_spec(plan_kind) else {
return Ok(None);
};
let plan_and_reports =
build_standard_stream_plan_and_reports(state, parts, trace_id, decision, body_json, spec)
.await?;
if plan_and_reports.is_empty() {
return Ok(None);
}
execute_stream_plan_and_reports(state, trace_id, decision, plan_kind, plan_and_reports).await
}
pub(crate) async fn maybe_execute_sync_via_local_standard_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> {
if let Some(response) = maybe_execute_sync_via_standard_family_decision(
state,
parts,
trace_id,
decision,
body_json,
plan_kind,
|plan_kind| {
claude::chat::resolve_sync_spec(plan_kind)
.or_else(|| claude::cli::resolve_sync_spec(plan_kind))
},
)
.await?
{
return Ok(Some(response));
}
maybe_execute_sync_via_standard_family_decision(
state,
parts,
trace_id,
decision,
body_json,
plan_kind,
|plan_kind| {
gemini::chat::resolve_sync_spec(plan_kind)
.or_else(|| gemini::cli::resolve_sync_spec(plan_kind))
},
)
.await
}
pub(crate) async fn maybe_execute_stream_via_local_standard_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> {
if let Some(response) = maybe_execute_stream_via_standard_family_decision(
state,
parts,
trace_id,
decision,
body_json,
plan_kind,
|plan_kind| {
claude::chat::resolve_stream_spec(plan_kind)
.or_else(|| claude::cli::resolve_stream_spec(plan_kind))
},
)
.await?
{
return Ok(Some(response));
}
maybe_execute_stream_via_standard_family_decision(
state,
parts,
trace_id,
decision,
body_json,
plan_kind,
|plan_kind| {
gemini::chat::resolve_stream_spec(plan_kind)
.or_else(|| gemini::cli::resolve_stream_spec(plan_kind))
},
)
.await
}
pub(crate) async fn maybe_execute_sync_via_local_same_format_provider_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> {
let Some(spec) = resolve_same_format_sync_spec(plan_kind) else {
return Ok(None);
};
let plan_and_reports =
build_same_format_sync_plan_and_reports(state, parts, trace_id, decision, body_json, spec)
.await?;
if plan_and_reports.is_empty() {
return Ok(None);
}
execute_sync_plan_and_reports(
state,
parts,
trace_id,
decision,
plan_kind,
plan_and_reports,
)
.await
}
pub(crate) async fn maybe_execute_stream_via_local_same_format_provider_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> {
let Some(spec) = resolve_same_format_stream_spec(plan_kind) else {
return Ok(None);
};
let plan_and_reports = build_same_format_stream_plan_and_reports(
state, parts, trace_id, decision, body_json, spec,
)
.await?;
if plan_and_reports.is_empty() {
return Ok(None);
}
execute_stream_plan_and_reports(state, trace_id, decision, plan_kind, plan_and_reports).await
}
pub(crate) async fn maybe_execute_sync_via_local_gemini_files_decision(
state: &AppState,
parts: &http::request::Parts,
body_json: &serde_json::Value,
body_base64: Option<&str>,
body_is_empty: bool,
trace_id: &str,
decision: &GatewayControlDecision,
plan_kind: &str,
) -> Result<Option<Response<Body>>, GatewayError> {
let plan_and_reports =
planner::specialized::files::build_local_gemini_files_sync_plan_and_reports_for_kind(
state,
parts,
body_json,
body_base64,
body_is_empty,
trace_id,
decision,
plan_kind,
)
.await?;
if plan_and_reports.is_empty() {
return Ok(None);
}
execute_sync_plan_and_reports(
state,
parts,
trace_id,
decision,
plan_kind,
plan_and_reports,
)
.await
}
pub(crate) async fn maybe_execute_stream_via_local_gemini_files_decision(
state: &AppState,
parts: &http::request::Parts,
trace_id: &str,
decision: &GatewayControlDecision,
plan_kind: &str,
) -> Result<Option<Response<Body>>, GatewayError> {
let plan_and_reports =
planner::specialized::files::build_local_gemini_files_stream_plan_and_reports_for_kind(
state, parts, trace_id, decision, plan_kind,
)
.await?;
if plan_and_reports.is_empty() {
return Ok(None);
}
execute_stream_plan_and_reports(state, trace_id, decision, plan_kind, plan_and_reports).await
}
pub(crate) async fn maybe_execute_sync_via_local_video_decision(
state: &AppState,
parts: &http::request::Parts,
body_json: &serde_json::Value,
trace_id: &str,
decision: &GatewayControlDecision,
plan_kind: &str,
) -> Result<Option<Response<Body>>, GatewayError> {
let plan_and_reports =
planner::specialized::video::build_local_video_sync_plan_and_reports_for_kind(
state, parts, body_json, trace_id, decision, plan_kind,
)
.await?;
if plan_and_reports.is_empty() {
return Ok(None);
}
execute_sync_plan_and_reports(
state,
parts,
trace_id,
decision,
plan_kind,
plan_and_reports,
)
.await
}
pub(crate) async fn maybe_execute_sync_request(
state: &AppState,
parts: &http::request::Parts,
body_bytes: &axum::body::Bytes,
trace_id: &str,
decision: Option<&GatewayControlDecision>,
) -> Result<Option<Response<Body>>, GatewayError> {
runtime::maybe_execute_sync_request(state, parts, body_bytes, trace_id, decision).await
}
pub(crate) async fn maybe_execute_stream_request(
state: &AppState,
parts: &http::request::Parts,
body_bytes: &axum::body::Bytes,
trace_id: &str,
decision: Option<&GatewayControlDecision>,
) -> Result<Option<Response<Body>>, GatewayError> {
runtime::maybe_execute_stream_request(state, parts, body_bytes, trace_id, decision).await
}
pub(crate) fn planner_decision_action(action: &str) -> bool {
matches!(
action,
EXECUTION_RUNTIME_SYNC_DECISION_ACTION | EXECUTION_RUNTIME_STREAM_DECISION_ACTION
)
}
pub(crate) fn parse_local_request_body(
parts: &http::request::Parts,
body_bytes: &axum::body::Bytes,
) -> Option<(serde_json::Value, Option<String>)> {
parse_direct_request_body(parts, body_bytes)
}
pub(crate) fn decision_payload_is_direct_execution(
payload: &GatewayControlSyncDecisionResponse,
) -> bool {
planner_decision_action(payload.action.as_str())
}

View File

@@ -0,0 +1,3 @@
pub(crate) use crate::gateway::ai_pipeline::planner::plan_builders::{
LocalStreamPlanAndReport, LocalSyncPlanAndReport,
};

View File

@@ -0,0 +1 @@
// Retry policy stays unchanged; executor uses existing execution_runtime behavior.

View File

@@ -18,13 +18,15 @@ mod control;
mod error;
#[path = "execution_runtime/mod.rs"]
mod execution_runtime;
#[path = "executor/mod.rs"]
mod executor;
#[path = "fallback_metrics.rs"]
mod fallback_metrics;
#[path = "gateway_cache/mod.rs"]
mod gateway_cache;
#[path = "gateway_data/mod.rs"]
mod gateway_data;
#[path = "handlers.rs"]
#[path = "handlers/mod.rs"]
mod handlers;
#[path = "headers.rs"]
mod headers;
@@ -35,17 +37,13 @@ mod intent;
#[path = "maintenance/mod.rs"]
mod maintenance;
#[path = "middleware/mod.rs"]
mod middleware;
pub(crate) mod middleware;
#[path = "model_fetch/mod.rs"]
mod model_fetch;
#[path = "provider_transport/mod.rs"]
mod provider_transport;
#[path = "rate_limit.rs"]
mod rate_limit;
#[path = "request_candidates.rs"]
mod request_candidates;
#[path = "response.rs"]
mod response;
#[path = "gateway/router.rs"]
mod router;
#[path = "scheduler/mod.rs"]

View File

@@ -20,6 +20,30 @@ pub(crate) use std::time::Duration;
pub(crate) use tokio::task::JoinHandle;
pub(crate) use aether_crypto::encrypt_python_fernet_plaintext;
pub(crate) use ai_pipeline::contracts::{
GatewayControlPlanRequest, GatewayControlPlanResponse, GatewayControlSyncDecisionResponse,
CLAUDE_CHAT_STREAM_PLAN_KIND, CLAUDE_CHAT_SYNC_PLAN_KIND, CLAUDE_CLI_STREAM_PLAN_KIND,
CLAUDE_CLI_SYNC_PLAN_KIND, EXECUTION_RUNTIME_STREAM_ACTION,
EXECUTION_RUNTIME_STREAM_DECISION_ACTION, EXECUTION_RUNTIME_SYNC_ACTION,
EXECUTION_RUNTIME_SYNC_DECISION_ACTION, GEMINI_CHAT_STREAM_PLAN_KIND,
GEMINI_CHAT_SYNC_PLAN_KIND, GEMINI_CLI_STREAM_PLAN_KIND, GEMINI_CLI_SYNC_PLAN_KIND,
GEMINI_FILES_DELETE_PLAN_KIND, GEMINI_FILES_DOWNLOAD_PLAN_KIND, GEMINI_FILES_GET_PLAN_KIND,
GEMINI_FILES_LIST_PLAN_KIND, GEMINI_FILES_UPLOAD_PLAN_KIND, GEMINI_VIDEO_CANCEL_SYNC_PLAN_KIND,
GEMINI_VIDEO_CREATE_SYNC_PLAN_KIND, OPENAI_CHAT_STREAM_PLAN_KIND, OPENAI_CHAT_SYNC_PLAN_KIND,
OPENAI_CLI_STREAM_PLAN_KIND, OPENAI_CLI_SYNC_PLAN_KIND, OPENAI_COMPACT_STREAM_PLAN_KIND,
OPENAI_COMPACT_SYNC_PLAN_KIND, OPENAI_VIDEO_CANCEL_SYNC_PLAN_KIND,
OPENAI_VIDEO_CONTENT_PLAN_KIND, OPENAI_VIDEO_CREATE_SYNC_PLAN_KIND,
OPENAI_VIDEO_DELETE_SYNC_PLAN_KIND, OPENAI_VIDEO_REMIX_SYNC_PLAN_KIND,
};
pub(crate) use ai_pipeline::finalize::{
maybe_build_stream_response_rewriter, maybe_build_sync_finalize_outcome,
maybe_compile_sync_finalize_response,
};
pub(crate) use ai_pipeline::planner::{
generic_decision_missing_exact_provider_request, maybe_build_stream_decision_payload,
maybe_build_stream_plan_payload, maybe_build_sync_decision_payload,
maybe_build_sync_plan_payload, resolve_stream_plan_kind, resolve_sync_plan_kind,
};
pub(crate) use async_task::video as video_tasks;
pub(crate) use async_task::VideoTaskService;
pub use async_task::VideoTaskTruthSourceMode;
@@ -82,37 +106,11 @@ pub(crate) use maintenance::{
spawn_gemini_file_mapping_cleanup_worker, spawn_provider_checkin_worker,
spawn_request_candidate_cleanup_worker,
};
pub use middleware::strip_cf_headers_middleware;
pub(crate) use model_fetch::spawn_model_fetch_worker;
pub(crate) use model_fetch::{perform_model_fetch_once, ModelFetchRunSummary};
pub use rate_limit::FrontdoorUserRpmConfig;
pub(crate) use rate_limit::{FrontdoorUserRpmLimiter, FrontdoorUserRpmOutcome};
pub(crate) use ai_pipeline::planner::{
generic_decision_missing_exact_provider_request, maybe_build_stream_decision_payload,
maybe_build_stream_plan_payload, maybe_build_sync_decision_payload,
maybe_build_sync_plan_payload, resolve_stream_plan_kind, resolve_sync_plan_kind,
GatewayControlPlanRequest, GatewayControlPlanResponse, GatewayControlSyncDecisionResponse,
CLAUDE_CHAT_STREAM_PLAN_KIND, CLAUDE_CHAT_SYNC_PLAN_KIND, CLAUDE_CLI_STREAM_PLAN_KIND,
CLAUDE_CLI_SYNC_PLAN_KIND, EXECUTION_RUNTIME_STREAM_ACTION,
EXECUTION_RUNTIME_STREAM_DECISION_ACTION, EXECUTION_RUNTIME_SYNC_ACTION,
EXECUTION_RUNTIME_SYNC_DECISION_ACTION, GEMINI_CHAT_STREAM_PLAN_KIND,
GEMINI_CHAT_SYNC_PLAN_KIND, GEMINI_CLI_STREAM_PLAN_KIND, GEMINI_CLI_SYNC_PLAN_KIND,
GEMINI_FILES_DELETE_PLAN_KIND, GEMINI_FILES_DOWNLOAD_PLAN_KIND, GEMINI_FILES_GET_PLAN_KIND,
GEMINI_FILES_LIST_PLAN_KIND, GEMINI_FILES_UPLOAD_PLAN_KIND, GEMINI_VIDEO_CANCEL_SYNC_PLAN_KIND,
GEMINI_VIDEO_CREATE_SYNC_PLAN_KIND, OPENAI_CHAT_STREAM_PLAN_KIND, OPENAI_CHAT_SYNC_PLAN_KIND,
OPENAI_CLI_STREAM_PLAN_KIND, OPENAI_CLI_SYNC_PLAN_KIND, OPENAI_COMPACT_STREAM_PLAN_KIND,
OPENAI_COMPACT_SYNC_PLAN_KIND, OPENAI_VIDEO_CANCEL_SYNC_PLAN_KIND,
OPENAI_VIDEO_CONTENT_PLAN_KIND, OPENAI_VIDEO_CREATE_SYNC_PLAN_KIND,
OPENAI_VIDEO_DELETE_SYNC_PLAN_KIND, OPENAI_VIDEO_REMIX_SYNC_PLAN_KIND,
};
pub(crate) use response::{
attach_control_metadata_headers, build_client_response, build_client_response_from_parts,
build_local_auth_rejection_response, build_local_http_error_response,
build_local_overloaded_response, build_local_user_rpm_limited_response,
};
pub(crate) use ai_pipeline::finalize::{
maybe_build_stream_response_rewriter, maybe_build_sync_finalize_outcome,
maybe_compile_sync_finalize_response,
};
pub use router::{build_router, build_router_with_state, serve_tcp};
pub(crate) use router::{metrics, RequestAdmissionError};
pub(crate) use state::{

View File

@@ -1744,7 +1744,10 @@ async fn gateway_refreshes_admin_provider_oauth_key_locally_with_trusted_admin_p
.as_bool()
.expect("account_state_recheck_attempted should be bool");
if account_state_recheck_attempted {
assert_eq!(payload["account_state_recheck_error"], "wham/usage API 返回状态码 401");
assert_eq!(
payload["account_state_recheck_error"],
"wham/usage API 返回状态码 401"
);
} else {
assert_eq!(
payload["account_state_recheck_error"],

View File

@@ -53,7 +53,7 @@ fn build_admin_model_catalog_maintenance_response() -> Response<Body> {
.into_response()
}
pub(super) async fn maybe_build_local_admin_core_response(
pub(crate) async fn maybe_build_local_admin_core_response(
state: &AppState,
request_context: &GatewayPublicRequestContext,
request_body: Option<&axum::body::Bytes>,

View File

@@ -9,7 +9,7 @@ mod endpoints_routes;
#[path = "endpoints/rpm.rs"]
mod endpoints_rpm;
pub(super) async fn maybe_build_local_admin_endpoints_response(
pub(crate) async fn maybe_build_local_admin_endpoints_response(
state: &AppState,
request_context: &GatewayPublicRequestContext,
request_body: Option<&axum::body::Bytes>,

View File

@@ -0,0 +1,74 @@
pub(crate) use super::*;
mod adaptive;
mod api_keys;
mod billing;
mod catalog_write_helpers;
mod core;
mod endpoints;
mod endpoints_health_helpers;
mod gemini_files;
mod global_models;
mod ldap;
mod misc_helpers;
mod models_helpers;
mod monitoring;
mod oauth_helpers;
mod payments;
mod pool;
mod provider_models;
#[path = "provider_oauth/dispatch.rs"]
mod provider_oauth_dispatch;
#[path = "provider_oauth/quota.rs"]
mod provider_oauth_quota;
#[path = "provider_oauth/refresh.rs"]
mod provider_oauth_refresh;
#[path = "provider_oauth/state.rs"]
mod provider_oauth_state;
mod provider_ops;
mod provider_query;
mod provider_strategy;
mod providers;
mod providers_helpers;
mod proxy_nodes;
mod security;
mod stats;
mod usage;
mod users;
mod video_tasks;
mod wallets;
pub(crate) use self::adaptive::*;
pub(crate) use self::api_keys::*;
pub(crate) use self::billing::*;
pub(crate) use self::catalog_write_helpers::*;
pub(crate) use self::core::*;
pub(crate) use self::endpoints::*;
pub(crate) use self::endpoints_health_helpers::*;
pub(crate) use self::gemini_files::*;
pub(crate) use self::global_models::*;
pub(crate) use self::ldap::*;
pub(crate) use self::misc_helpers::*;
pub(crate) use self::models_helpers::*;
pub(crate) use self::monitoring::maybe_build_local_admin_monitoring_root_response as maybe_build_local_admin_monitoring_response;
pub(crate) use self::oauth_helpers::*;
pub(crate) use self::payments::*;
pub(crate) use self::pool::*;
pub(crate) use self::provider_models::*;
pub(crate) use self::provider_oauth_dispatch::*;
pub(crate) use self::provider_oauth_quota::*;
pub(crate) use self::provider_oauth_refresh::*;
pub(crate) use self::provider_oauth_state::*;
pub(crate) use self::provider_ops::admin_provider_ops_local_action_response;
pub(crate) use self::provider_ops::maybe_build_local_admin_provider_ops_response;
pub(crate) use self::provider_query::*;
pub(crate) use self::provider_strategy::*;
pub(crate) use self::providers::*;
pub(crate) use self::providers_helpers::*;
pub(crate) use self::proxy_nodes::*;
pub(crate) use self::security::*;
pub(crate) use self::stats::*;
pub(crate) use self::usage::*;
pub(crate) use self::users::*;
pub(crate) use self::video_tasks::*;
pub(crate) use self::wallets::*;

View File

@@ -19,7 +19,7 @@ mod provider_models_list;
#[path = "provider_models/update.rs"]
mod provider_models_update;
pub(super) async fn maybe_build_local_admin_provider_models_response(
pub(crate) async fn maybe_build_local_admin_provider_models_response(
state: &AppState,
request_context: &GatewayPublicRequestContext,
request_body: Option<&axum::body::Bytes>,

View File

@@ -15,7 +15,7 @@ mod dispatch_start;
#[path = "dispatch/tasks.rs"]
mod dispatch_tasks;
pub(super) async fn maybe_build_local_admin_provider_oauth_response(
pub(crate) async fn maybe_build_local_admin_provider_oauth_response(
state: &AppState,
request_context: &GatewayPublicRequestContext,
request_body: Option<&axum::body::Bytes>,

View File

@@ -0,0 +1,6 @@
pub(crate) use super::*;
mod gateway_helpers;
pub(crate) use self::gateway_helpers::*;
mod gateway;
pub(crate) use self::gateway::maybe_build_local_internal_proxy_response_impl;

View File

@@ -55,6 +55,10 @@ use crate::gateway::api::ai::{
admin_default_body_rules_for_signature, admin_endpoint_signature_parts,
public_api_format_local_path,
};
use crate::gateway::api::response::{
build_client_response, build_local_auth_rejection_response, build_local_http_error_response,
build_local_overloaded_response, build_local_user_rpm_limited_response,
};
use crate::gateway::constants::*;
use crate::gateway::headers::{
extract_or_generate_trace_id, header_value_str, should_skip_request_header,
@@ -68,9 +72,7 @@ use crate::gateway::scheduler::{
provider_key_health_score,
};
use crate::gateway::{
allows_control_execute_emergency, build_client_response, build_local_auth_rejection_response,
build_local_http_error_response, build_local_overloaded_response,
build_local_user_rpm_limited_response, execute_execution_runtime_stream,
allows_control_execute_emergency, execute_execution_runtime_stream,
execute_execution_runtime_sync, maybe_build_stream_decision_payload,
maybe_build_stream_plan_payload, maybe_build_sync_decision_payload,
maybe_build_sync_finalize_outcome, maybe_build_sync_plan_payload, maybe_execute_via_control,
@@ -91,11 +93,17 @@ const ADMIN_EXTERNAL_MODELS_CACHE_TTL_SECS: u64 = 15 * 60;
const ADMIN_PROVIDER_OAUTH_RUST_BACKEND_DETAIL: &str =
"Admin provider OAuth requires Rust maintenance backend";
#[path = "handlers/proxy.rs"]
mod proxy;
pub(crate) mod admin;
pub(crate) mod internal;
pub(crate) mod proxy;
pub(crate) mod public;
pub(crate) mod shared;
use proxy::matches_model_mapping_for_models;
pub(crate) use admin::*;
pub(crate) use internal::*;
pub(crate) use public::*;
pub(crate) use proxy::proxy_request;
pub(crate) use shared::*;
const OFFICIAL_EXTERNAL_MODEL_PROVIDERS: &[&str] = &[
"anthropic",
@@ -134,102 +142,4 @@ pub(crate) struct AdminProviderPoolRuntimeState {
pub(crate) lru_score_by_key: BTreeMap<String, f64>,
}
#[path = "handlers/admin/adaptive.rs"]
mod admin_adaptive_handler;
#[path = "handlers/admin/api_keys.rs"]
mod admin_api_keys_handler;
#[path = "handlers/admin/billing.rs"]
mod admin_billing_handler;
#[path = "handlers/admin/endpoints_health_helpers.rs"]
mod admin_endpoints_health_helpers;
#[path = "handlers/admin/gemini_files.rs"]
mod admin_gemini_files_handler;
#[path = "handlers/admin/ldap.rs"]
mod admin_ldap_handler;
#[path = "handlers/admin/models_helpers.rs"]
mod admin_models_helpers;
#[path = "handlers/admin/monitoring.rs"]
mod admin_monitoring_handler;
#[path = "handlers/admin/oauth_helpers.rs"]
mod admin_oauth_helpers;
#[path = "handlers/admin/payments.rs"]
mod admin_payments_handler;
#[path = "handlers/admin/pool.rs"]
mod admin_pool_handler;
#[path = "handlers/admin/provider_oauth/quota.rs"]
mod admin_provider_oauth_quota;
#[path = "handlers/admin/provider_oauth/refresh.rs"]
mod admin_provider_oauth_refresh;
#[path = "handlers/admin/provider_oauth/state.rs"]
mod admin_provider_oauth_state;
#[path = "handlers/admin/provider_ops.rs"]
mod admin_provider_ops;
#[path = "handlers/admin/provider_query.rs"]
mod admin_provider_query_handler;
#[path = "handlers/admin/provider_strategy.rs"]
mod admin_provider_strategy_handler;
#[path = "handlers/admin/providers_helpers.rs"]
mod admin_providers_helpers;
#[path = "handlers/admin/proxy_nodes.rs"]
mod admin_proxy_nodes_handler;
#[path = "handlers/admin/security.rs"]
mod admin_security_handler;
#[path = "handlers/admin/stats.rs"]
mod admin_stats_handler;
#[path = "handlers/admin/usage.rs"]
mod admin_usage_handler;
#[path = "handlers/admin/users.rs"]
mod admin_users_handler;
#[path = "handlers/admin/video_tasks.rs"]
mod admin_video_tasks_handler;
#[path = "handlers/admin/wallets.rs"]
mod admin_wallets_handler;
#[path = "handlers/internal/gateway.rs"]
mod internal_gateway;
#[path = "handlers/internal/gateway_helpers.rs"]
mod internal_gateway_helpers;
#[path = "handlers/public/catalog_helpers.rs"]
mod public_catalog_helpers;
#[path = "handlers/public/system_modules_helpers.rs"]
mod public_system_modules_helpers;
#[path = "handlers/shared.rs"]
mod shared;
#[path = "handlers/admin/catalog_write_helpers.rs"]
mod admin_catalog_write_helpers;
pub(crate) use self::admin_catalog_write_helpers::*;
#[path = "handlers/admin/misc_helpers.rs"]
mod admin_misc_helpers;
pub(crate) use self::admin_misc_helpers::*;
pub(crate) use self::admin_adaptive_handler::*;
pub(crate) use self::admin_api_keys_handler::*;
pub(crate) use self::admin_billing_handler::*;
pub(crate) use self::admin_endpoints_health_helpers::*;
pub(crate) use self::admin_gemini_files_handler::*;
pub(crate) use self::admin_ldap_handler::*;
pub(crate) use self::admin_models_helpers::*;
pub(crate) use self::admin_monitoring_handler::maybe_build_local_admin_monitoring_root_response as maybe_build_local_admin_monitoring_response;
pub(crate) use self::admin_oauth_helpers::*;
pub(crate) use self::admin_payments_handler::*;
pub(crate) use self::admin_pool_handler::*;
pub(crate) use self::admin_provider_oauth_quota::*;
pub(crate) use self::admin_provider_oauth_refresh::*;
pub(crate) use self::admin_provider_oauth_state::*;
pub(crate) use self::admin_provider_ops::admin_provider_ops_local_action_response;
pub(crate) use self::admin_provider_ops::maybe_build_local_admin_provider_ops_response;
pub(crate) use self::admin_provider_query_handler::*;
pub(crate) use self::admin_provider_strategy_handler::*;
pub(crate) use self::admin_providers_helpers::*;
pub(crate) use self::admin_proxy_nodes_handler::*;
pub(crate) use self::admin_security_handler::*;
pub(crate) use self::admin_stats_handler::*;
pub(crate) use self::admin_usage_handler::*;
pub(crate) use self::admin_users_handler::*;
pub(crate) use self::admin_video_tasks_handler::*;
pub(crate) use self::admin_wallets_handler::*;
pub(crate) use self::internal_gateway::maybe_build_local_internal_proxy_response_impl;
pub(crate) use self::internal_gateway_helpers::*;
pub(crate) use self::public_catalog_helpers::*;
pub(crate) use self::public_system_modules_helpers::*;
pub(crate) use self::shared::*;

View File

@@ -1,3 +1,4 @@
use super::super::{admin, internal, public};
use super::*;
pub(super) async fn maybe_build_local_internal_proxy_response(
@@ -7,7 +8,7 @@ pub(super) async fn maybe_build_local_internal_proxy_response(
request_body: Option<&axum::body::Bytes>,
legacy_internal_gateway_allowed: bool,
) -> Result<Option<Response<Body>>, GatewayError> {
let response = maybe_build_local_internal_proxy_response_impl(
let response = internal::maybe_build_local_internal_proxy_response_impl(
state,
request_context,
remote_addr,
@@ -22,7 +23,7 @@ pub(super) async fn maybe_build_local_internal_proxy_response(
.and_then(|decision| decision.route_family.as_deref())
== Some("gateway_legacy")
{
return Ok(response.map(attach_legacy_internal_gateway_deprecation_headers));
return Ok(response.map(internal::attach_legacy_internal_gateway_deprecation_headers));
}
Ok(response)
@@ -44,27 +45,33 @@ pub(super) async fn maybe_build_local_admin_proxy_response(
}
if let Some(response) =
super::admin_provider_oauth_dispatch::maybe_build_local_admin_provider_oauth_response(
state,
request_context,
request_body,
)
.await?
admin::maybe_build_local_admin_provider_oauth_response(state, request_context, request_body)
.await?
{
return Ok(Some(response));
}
if let Some(response) = super::public_support::maybe_build_local_admin_announcements_response(
state,
request_context,
request_body,
)
.await?
if let Some(response) =
public::maybe_build_local_admin_announcements_response(state, request_context, request_body)
.await?
{
return Ok(Some(response));
}
if let Some(response) = super::admin_core::maybe_build_local_admin_core_response(
if let Some(response) =
admin::maybe_build_local_admin_core_response(state, request_context, request_body).await?
{
return Ok(Some(response));
}
if let Some(response) =
admin::maybe_build_local_admin_global_models_response(state, request_context, request_body)
.await?
{
return Ok(Some(response));
}
if let Some(response) = admin::maybe_build_local_admin_provider_models_response(
state,
request_context,
request_body,
@@ -75,127 +82,24 @@ pub(super) async fn maybe_build_local_admin_proxy_response(
}
if let Some(response) =
super::admin_global_models::maybe_build_local_admin_global_models_response(
state,
request_context,
request_body,
)
.await?
{
return Ok(Some(response));
}
if let Some(response) =
super::admin_provider_models::maybe_build_local_admin_provider_models_response(
state,
request_context,
request_body,
)
.await?
{
return Ok(Some(response));
}
if let Some(response) = super::admin_providers::maybe_build_local_admin_providers_response(
state,
request_context,
request_body,
)
.await?
{
return Ok(Some(response));
}
if let Some(response) =
maybe_build_local_admin_provider_ops_response(state, request_context, request_body).await?
{
return Ok(Some(response));
}
if let Some(response) =
maybe_build_local_admin_adaptive_response(state, request_context, request_body).await?
{
return Ok(Some(response));
}
if let Some(response) =
maybe_build_local_admin_provider_strategy_response(state, request_context, request_body)
admin::maybe_build_local_admin_providers_response(state, request_context, request_body)
.await?
{
return Ok(Some(response));
}
if let Some(response) =
maybe_build_local_admin_pool_response(state, request_context, request_body).await?
{
return Ok(Some(response));
}
if let Some(response) =
maybe_build_local_admin_billing_response(state, request_context, request_body).await?
{
return Ok(Some(response));
}
if let Some(response) =
maybe_build_local_admin_payments_response(state, request_context, request_body).await?
{
return Ok(Some(response));
}
if let Some(response) =
maybe_build_local_admin_provider_query_response(state, request_context, request_body)
admin::maybe_build_local_admin_provider_ops_response(state, request_context, request_body)
.await?
{
return Ok(Some(response));
}
if let Some(response) =
maybe_build_local_admin_security_response(state, request_context, request_body).await?
admin::maybe_build_local_admin_adaptive_response(state, request_context, request_body)
.await?
{
return Ok(Some(response));
}
if let Some(response) = maybe_build_local_admin_stats_response(state, request_context).await? {
return Ok(Some(response));
}
if let Some(response) =
maybe_build_local_admin_monitoring_response(state, request_context).await?
{
return Ok(Some(response));
}
if let Some(response) =
maybe_build_local_admin_usage_response(state, request_context, request_body).await?
{
return Ok(Some(response));
}
if let Some(response) =
maybe_build_local_admin_video_tasks_response(state, request_context).await?
{
return Ok(Some(response));
}
if let Some(response) =
maybe_build_local_admin_proxy_nodes_response(state, request_context).await?
{
return Ok(Some(response));
}
if let Some(response) =
maybe_build_local_admin_wallets_response(state, request_context, request_body).await?
{
return Ok(Some(response));
}
if let Some(response) =
maybe_build_local_admin_api_keys_response(state, request_context, request_body).await?
{
return Ok(Some(response));
}
if let Some(response) =
maybe_build_local_admin_ldap_response(state, request_context, request_body).await?
{
return Ok(Some(response));
}
if let Some(response) =
maybe_build_local_admin_gemini_files_response(state, request_context, request_body).await?
{
return Ok(Some(response));
}
if let Some(response) =
maybe_build_local_admin_users_response(state, request_context, request_body).await?
{
return Ok(Some(response));
}
if let Some(response) = super::admin_endpoints::maybe_build_local_admin_endpoints_response(
if let Some(response) = admin::maybe_build_local_admin_provider_strategy_response(
state,
request_context,
request_body,
@@ -204,6 +108,94 @@ pub(super) async fn maybe_build_local_admin_proxy_response(
{
return Ok(Some(response));
}
if let Some(response) =
admin::maybe_build_local_admin_pool_response(state, request_context, request_body).await?
{
return Ok(Some(response));
}
if let Some(response) =
admin::maybe_build_local_admin_billing_response(state, request_context, request_body)
.await?
{
return Ok(Some(response));
}
if let Some(response) =
admin::maybe_build_local_admin_payments_response(state, request_context, request_body)
.await?
{
return Ok(Some(response));
}
if let Some(response) =
admin::maybe_build_local_admin_provider_query_response(state, request_context, request_body)
.await?
{
return Ok(Some(response));
}
if let Some(response) =
admin::maybe_build_local_admin_security_response(state, request_context, request_body)
.await?
{
return Ok(Some(response));
}
if let Some(response) =
admin::maybe_build_local_admin_stats_response(state, request_context).await?
{
return Ok(Some(response));
}
if let Some(response) =
admin::maybe_build_local_admin_monitoring_response(state, request_context).await?
{
return Ok(Some(response));
}
if let Some(response) =
admin::maybe_build_local_admin_usage_response(state, request_context, request_body).await?
{
return Ok(Some(response));
}
if let Some(response) =
admin::maybe_build_local_admin_video_tasks_response(state, request_context).await?
{
return Ok(Some(response));
}
if let Some(response) =
admin::maybe_build_local_admin_proxy_nodes_response(state, request_context).await?
{
return Ok(Some(response));
}
if let Some(response) =
admin::maybe_build_local_admin_wallets_response(state, request_context, request_body)
.await?
{
return Ok(Some(response));
}
if let Some(response) =
admin::maybe_build_local_admin_api_keys_response(state, request_context, request_body)
.await?
{
return Ok(Some(response));
}
if let Some(response) =
admin::maybe_build_local_admin_ldap_response(state, request_context, request_body).await?
{
return Ok(Some(response));
}
if let Some(response) =
admin::maybe_build_local_admin_gemini_files_response(state, request_context, request_body)
.await?
{
return Ok(Some(response));
}
if let Some(response) =
admin::maybe_build_local_admin_users_response(state, request_context, request_body).await?
{
return Ok(Some(response));
}
if let Some(response) =
admin::maybe_build_local_admin_endpoints_response(state, request_context, request_body)
.await?
{
return Ok(Some(response));
}
Ok(None)
}

View File

@@ -1,27 +1,14 @@
pub(crate) use super::*;
#[path = "proxy/local.rs"]
mod local;
#[path = "admin/core.rs"]
mod admin_core;
#[path = "admin/endpoints.rs"]
mod admin_endpoints;
#[path = "admin/global_models.rs"]
mod admin_global_models;
#[path = "admin/provider_models.rs"]
mod admin_provider_models;
#[path = "admin/provider_oauth/dispatch.rs"]
mod admin_provider_oauth_dispatch;
#[path = "admin/providers.rs"]
mod admin_providers;
#[path = "public/support.rs"]
mod public_support;
use self::local::{
maybe_build_local_admin_proxy_response, maybe_build_local_internal_proxy_response,
};
pub(crate) use self::public_support::matches_model_mapping_for_models;
use super::internal::{
attach_legacy_internal_gateway_deprecation_headers, resolve_local_proxy_execution_path,
};
pub(crate) use super::public::matches_model_mapping_for_models;
use crate::gateway::ai_pipeline::{finalize as ai_finalize, runtime as ai_runtime};
const OPENAI_CHAT_PYTHON_FALLBACK_REMOVED_DETAIL: &str =
@@ -362,7 +349,7 @@ pub(crate) async fn proxy_request(
request_permit.take(),
));
}
if let Some(response) = public_support::maybe_build_local_public_support_response(
if let Some(response) = super::public::maybe_build_local_public_support_response(
&state,
&request_context,
&parts.headers,
@@ -882,7 +869,7 @@ fn local_execution_runtime_miss_detail_after_python_fallback_removal(
}
}
#[path = "proxy/finalize.rs"]
#[path = "finalize.rs"]
mod finalize;
use self::finalize::{

Some files were not shown because too many files have changed in this diff Show More