mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-03 01:40:21 +08:00
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:
156
apps/aether-gateway/src/handlers/public/support/models/shared.rs
Normal file
156
apps/aether-gateway/src/handlers/public/support/models/shared.rs
Normal file
@@ -0,0 +1,156 @@
|
||||
use super::*;
|
||||
|
||||
pub(crate) fn models_api_format(request_context: &GatewayPublicRequestContext) -> Option<&str> {
|
||||
request_context
|
||||
.control_decision
|
||||
.as_ref()
|
||||
.and_then(|decision| decision.auth_endpoint_signature.as_deref())
|
||||
.filter(|signature| matches!(*signature, "openai:chat" | "claude:chat" | "gemini:chat"))
|
||||
}
|
||||
|
||||
pub(super) fn models_detail_id(request_path: &str) -> Option<String> {
|
||||
let raw = if let Some(value) = request_path.strip_prefix("/v1/models/") {
|
||||
value
|
||||
} else if let Some(value) = request_path.strip_prefix("/v1beta/models/") {
|
||||
value
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
let normalized = raw.trim().trim_start_matches("models/").trim();
|
||||
if normalized.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(normalized.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
fn auth_snapshot_allows_provider_for_models(
|
||||
auth_snapshot: Option<&crate::gateway::gateway_data::StoredGatewayAuthApiKeySnapshot>,
|
||||
provider_id: &str,
|
||||
provider_name: &str,
|
||||
) -> bool {
|
||||
let Some(allowed) = auth_snapshot.and_then(
|
||||
crate::gateway::gateway_data::StoredGatewayAuthApiKeySnapshot::effective_allowed_providers,
|
||||
) else {
|
||||
return true;
|
||||
};
|
||||
|
||||
allowed.iter().any(|value| {
|
||||
value.trim().eq_ignore_ascii_case(provider_id.trim())
|
||||
|| value.trim().eq_ignore_ascii_case(provider_name.trim())
|
||||
})
|
||||
}
|
||||
|
||||
fn auth_snapshot_allows_model_for_models(
|
||||
auth_snapshot: Option<&crate::gateway::gateway_data::StoredGatewayAuthApiKeySnapshot>,
|
||||
global_model_name: &str,
|
||||
) -> bool {
|
||||
let Some(allowed) = auth_snapshot.and_then(
|
||||
crate::gateway::gateway_data::StoredGatewayAuthApiKeySnapshot::effective_allowed_models,
|
||||
) else {
|
||||
return true;
|
||||
};
|
||||
allowed.iter().any(|value| value == global_model_name)
|
||||
}
|
||||
|
||||
fn mapping_scope_matches_for_models(
|
||||
mapping: &aether_data::repository::candidate_selection::StoredProviderModelMapping,
|
||||
api_format: &str,
|
||||
) -> bool {
|
||||
let Some(api_formats) = mapping.api_formats.as_ref() else {
|
||||
return true;
|
||||
};
|
||||
api_formats
|
||||
.iter()
|
||||
.any(|value| value.trim().eq_ignore_ascii_case(api_format))
|
||||
}
|
||||
|
||||
fn candidate_model_names_for_models(
|
||||
row: &StoredMinimalCandidateSelectionRow,
|
||||
api_format: &str,
|
||||
) -> std::collections::BTreeSet<String> {
|
||||
let mut names = std::collections::BTreeSet::from([row.model_provider_model_name.clone()]);
|
||||
if let Some(mappings) = row.model_provider_model_mappings.as_ref() {
|
||||
for mapping in mappings {
|
||||
if mapping_scope_matches_for_models(mapping, api_format) {
|
||||
names.insert(mapping.name.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
names
|
||||
}
|
||||
|
||||
pub(crate) fn matches_model_mapping_for_models(pattern: &str, model_name: &str) -> bool {
|
||||
let Ok(compiled) = Regex::new(&format!("^(?:{pattern})$")) else {
|
||||
return false;
|
||||
};
|
||||
compiled.is_match(model_name)
|
||||
}
|
||||
|
||||
fn row_exposes_global_model_for_models(
|
||||
row: &StoredMinimalCandidateSelectionRow,
|
||||
api_format: &str,
|
||||
) -> bool {
|
||||
let Some(key_allowed_models) = row.key_allowed_models.as_ref() else {
|
||||
return true;
|
||||
};
|
||||
if key_allowed_models.is_empty() {
|
||||
return false;
|
||||
}
|
||||
if key_allowed_models
|
||||
.iter()
|
||||
.any(|value| value == &row.global_model_name)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
let candidate_models = candidate_model_names_for_models(row, api_format);
|
||||
for allowed_model in key_allowed_models {
|
||||
if candidate_models.contains(allowed_model) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
let Some(global_model_mappings) = row.global_model_mappings.as_ref() else {
|
||||
return false;
|
||||
};
|
||||
for allowed_model in key_allowed_models {
|
||||
for pattern in global_model_mappings {
|
||||
if matches_model_mapping_for_models(pattern, allowed_model) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub(super) fn filter_rows_for_models(
|
||||
rows: Vec<StoredMinimalCandidateSelectionRow>,
|
||||
auth_snapshot: Option<&crate::gateway::gateway_data::StoredGatewayAuthApiKeySnapshot>,
|
||||
api_format: &str,
|
||||
) -> Vec<StoredMinimalCandidateSelectionRow> {
|
||||
let mut filtered = rows
|
||||
.into_iter()
|
||||
.filter(|row| {
|
||||
auth_snapshot_allows_provider_for_models(
|
||||
auth_snapshot,
|
||||
&row.provider_id,
|
||||
&row.provider_name,
|
||||
)
|
||||
})
|
||||
.filter(|row| auth_snapshot_allows_model_for_models(auth_snapshot, &row.global_model_name))
|
||||
.filter(|row| row_exposes_global_model_for_models(row, api_format))
|
||||
.collect::<Vec<_>>();
|
||||
filtered.sort_by(|left, right| left.global_model_name.cmp(&right.global_model_name));
|
||||
let mut deduped = Vec::new();
|
||||
let mut last_model_name: Option<String> = None;
|
||||
for row in filtered {
|
||||
if last_model_name.as_deref() == Some(row.global_model_name.as_str()) {
|
||||
continue;
|
||||
}
|
||||
last_model_name = Some(row.global_model_name.clone());
|
||||
deduped.push(row);
|
||||
}
|
||||
deduped
|
||||
}
|
||||
Reference in New Issue
Block a user