refactor: 移除独立 hub/proxy/executor/gateway crate,统一为 gateway tunnel 架构

- 删除 aether-hub、aether-proxy 独立项目及其 Dockerfile/配置
- 删除 crates/aether-executor 和 crates/aether-gateway 全部模块
- 新增 apps/ 目录作为应用入口
- 将 hub 概念重构为 gateway tunnel transport
- 将 executor 重构为 execution runtime
- 新增 tunnel.rs 合约定义和 testkit tunnel/execution_runtime 模块
- 更新 Python 服务层和测试适配新架构命名
This commit is contained in:
fawney19
2026-04-03 14:59:58 +08:00
parent ddf18fed9a
commit 8f26e1a31f
983 changed files with 103098 additions and 105837 deletions

View File

@@ -0,0 +1,14 @@
pub(crate) fn normalized_signature(api_format: &str) -> Option<&'static str> {
match api_format {
"claude:chat" => Some("claude:chat"),
"claude:cli" => Some("claude:cli"),
_ => None,
}
}
pub(crate) fn local_path(api_format: &str) -> Option<&'static str> {
match api_format {
"claude" | "claude:chat" | "claude:cli" => Some("/v1/messages"),
_ => None,
}
}

View File

@@ -0,0 +1,16 @@
pub(crate) fn normalized_signature(api_format: &str) -> Option<&'static str> {
match api_format {
"gemini:chat" => Some("gemini:chat"),
"gemini:cli" => Some("gemini:cli"),
"gemini:video" => Some("gemini:video"),
_ => None,
}
}
pub(crate) fn local_path(api_format: &str) -> Option<&'static str> {
match api_format {
"gemini" | "gemini:chat" | "gemini:cli" => Some("/v1beta/models/{model}:{action}"),
"gemini:video" => Some("/v1beta/models/{model}:predictLongRunning"),
_ => None,
}
}

View File

@@ -0,0 +1,9 @@
mod claude;
mod gemini;
mod openai;
mod registry;
pub(crate) use registry::{
admin_default_body_rules_for_signature, admin_endpoint_signature_parts, mount_ai_routes,
normalize_admin_endpoint_signature, public_api_format_local_path,
};

View File

@@ -0,0 +1,19 @@
pub(crate) fn normalized_signature(api_format: &str) -> Option<&'static str> {
match api_format {
"openai:chat" => Some("openai:chat"),
"openai:cli" => Some("openai:cli"),
"openai:compact" => Some("openai:compact"),
"openai:video" => Some("openai:video"),
_ => None,
}
}
pub(crate) fn local_path(api_format: &str) -> Option<&'static str> {
match api_format {
"openai" | "openai:chat" => Some("/v1/chat/completions"),
"openai:cli" => Some("/v1/responses"),
"openai:compact" => Some("/v1/responses/compact"),
"openai:video" => Some("/v1/videos"),
_ => None,
}
}

View File

@@ -0,0 +1,93 @@
use axum::routing::{any, post};
use axum::Router;
use serde_json::json;
use super::{claude, gemini, openai};
use crate::gateway::{proxy_request, AppState};
// Router registration patterns live here so AI public ingress has a single mount registry.
// They intentionally stay separate from manifest-facing route inventories in constants.rs,
// which describe operational compatibility surfaces rather than the concrete axum mount list.
const AI_POST_ROUTE_PATTERNS: &[&str] = &[
"/v1/chat/completions",
"/v1/messages",
"/v1/messages/count_tokens",
"/v1/responses",
"/v1/responses/compact",
];
const AI_ANY_ROUTE_PATTERNS: &[&str] = &[
"/v1/models/{*gemini_path}",
"/v1beta/models/{*gemini_path}",
"/v1beta/operations",
"/v1beta/operations/{*operation_path}",
"/v1/videos",
"/v1/videos/{*video_path}",
"/upload/v1beta/files",
"/v1beta/files",
"/v1beta/files/{*file_path}",
];
pub(crate) fn mount_ai_routes(mut router: Router<AppState>) -> Router<AppState> {
for path in AI_POST_ROUTE_PATTERNS {
router = router.route(path, post(proxy_request));
}
for path in AI_ANY_ROUTE_PATTERNS {
router = router.route(path, any(proxy_request));
}
router
}
pub(crate) fn public_api_format_local_path(api_format: &str) -> &'static str {
let normalized = api_format.trim().to_ascii_lowercase();
openai::local_path(&normalized)
.or_else(|| claude::local_path(&normalized))
.or_else(|| gemini::local_path(&normalized))
.unwrap_or("/")
}
pub(crate) fn normalize_admin_endpoint_signature(api_format: &str) -> Option<&'static str> {
let normalized = api_format.trim().to_ascii_lowercase();
openai::normalized_signature(&normalized)
.or_else(|| claude::normalized_signature(&normalized))
.or_else(|| gemini::normalized_signature(&normalized))
}
pub(crate) fn admin_endpoint_signature_parts(
api_format: &str,
) -> Option<(&'static str, &'static str, &'static str)> {
let normalized = normalize_admin_endpoint_signature(api_format)?;
let (api_family, endpoint_kind) = normalized.split_once(':')?;
Some((normalized, api_family, endpoint_kind))
}
fn codex_default_body_rules() -> Vec<serde_json::Value> {
vec![
json!({"action": "drop", "path": "max_output_tokens"}),
json!({"action": "drop", "path": "temperature"}),
json!({"action": "drop", "path": "top_p"}),
json!({"action": "set", "path": "store", "value": false}),
json!({
"action": "set",
"path": "instructions",
"value": "You are GPT-5.",
"condition": {"path": "instructions", "op": "not_exists"},
}),
]
}
pub(crate) fn admin_default_body_rules_for_signature(
api_format: &str,
provider_type: Option<&str>,
) -> Option<(String, Vec<serde_json::Value>)> {
let normalized_api_format = normalize_admin_endpoint_signature(api_format)?.to_string();
let provider_type = provider_type.map(|value| value.trim().to_ascii_lowercase());
let body_rules = if normalized_api_format == "openai:compact"
|| (normalized_api_format == "openai:cli" && provider_type.as_deref() == Some("codex"))
{
codex_default_body_rules()
} else {
Vec::new()
};
Some((normalized_api_format, body_rules))
}