mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-08 18:37:46 +08:00
feat(models): add external catalog proxy selection
This commit is contained in:
@@ -11,6 +11,7 @@ use aether_admin::provider::{
|
||||
use aether_data_contracts::repository::global_models::{
|
||||
AdminProviderModelListQuery, StoredAdminProviderModel, UpsertAdminProviderModelRecord,
|
||||
};
|
||||
use axum::http;
|
||||
use serde_json::json;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use uuid::Uuid;
|
||||
@@ -488,8 +489,24 @@ impl<'a> AdminAppState<'a> {
|
||||
|
||||
pub(crate) async fn read_admin_external_models_cache(
|
||||
&self,
|
||||
request_id: &str,
|
||||
) -> Result<Option<serde_json::Value>, GatewayError> {
|
||||
crate::handlers::admin::model::read_admin_external_models_cache(self).await
|
||||
crate::handlers::admin::model::read_admin_external_models_cache(self, request_id).await
|
||||
}
|
||||
|
||||
pub(crate) async fn build_admin_external_models_config_payload(
|
||||
&self,
|
||||
) -> Result<serde_json::Value, GatewayError> {
|
||||
crate::handlers::admin::model::build_admin_external_models_config_payload(self).await
|
||||
}
|
||||
|
||||
pub(crate) async fn apply_admin_external_models_config_update(
|
||||
&self,
|
||||
request_body: &axum::body::Bytes,
|
||||
) -> Result<Result<serde_json::Value, (http::StatusCode, serde_json::Value)>, GatewayError>
|
||||
{
|
||||
crate::handlers::admin::model::apply_admin_external_models_config_update(self, request_body)
|
||||
.await
|
||||
}
|
||||
|
||||
pub(crate) async fn clear_admin_external_models_cache(
|
||||
|
||||
@@ -2,6 +2,7 @@ use super::{AdminAppState, ADMIN_SYSTEM_DATA_EXPORT_VERSION};
|
||||
use crate::ai_serving::build_provider_key_pool_score_upsert;
|
||||
use crate::api::ai::admin_endpoint_signature_parts;
|
||||
use crate::handlers::admin::admin_provider_pool_config;
|
||||
use crate::handlers::admin::model::ADMIN_EXTERNAL_MODELS_PROXY_NODE_CONFIG_KEY;
|
||||
use crate::handlers::admin::provider::endpoints_admin::payloads::AdminProviderEndpointUpdatePatch;
|
||||
use crate::handlers::admin::provider::shared::payloads::{
|
||||
AdminProviderCreateRequest, AdminProviderKeyCreateRequest, AdminProviderKeyUpdatePatch,
|
||||
@@ -61,6 +62,15 @@ fn invalid_request(detail: impl Into<String>) -> (http::StatusCode, Value) {
|
||||
)
|
||||
}
|
||||
|
||||
fn normalize_imported_system_config_key(key: &str) -> String {
|
||||
let normalized = normalize_admin_system_config_key(key);
|
||||
if normalized.eq_ignore_ascii_case(ADMIN_EXTERNAL_MODELS_PROXY_NODE_CONFIG_KEY) {
|
||||
ADMIN_EXTERNAL_MODELS_PROXY_NODE_CONFIG_KEY.to_string()
|
||||
} else {
|
||||
normalized
|
||||
}
|
||||
}
|
||||
|
||||
fn build_admin_system_data_import_part_body(
|
||||
root: &Map<String, Value>,
|
||||
field_name: &str,
|
||||
@@ -1299,6 +1309,82 @@ impl<'a> AdminAppState<'a> {
|
||||
|
||||
let mut stats = AdminSystemConfigImportStats::default();
|
||||
|
||||
// Proxy nodes are deployment-local resources and are intentionally not imported by the
|
||||
// Rust admin backend. Apply the external catalog selector before importing any other
|
||||
// object, and turn a non-empty exported node reference into direct mode. This keeps a
|
||||
// clean-environment restore portable and prevents a late selector validation failure from
|
||||
// leaving the rest of the document partially imported.
|
||||
let (imported_external_models_configs, imported_system_configs): (Vec<_>, Vec<_>) =
|
||||
imported_system_configs.into_iter().partition(|item| {
|
||||
normalize_imported_system_config_key(&item.value.key)
|
||||
== ADMIN_EXTERNAL_MODELS_PROXY_NODE_CONFIG_KEY
|
||||
});
|
||||
let mut existing_system_config_keys = self
|
||||
.list_system_config_entries()
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|entry| normalize_imported_system_config_key(&entry.key))
|
||||
.collect::<BTreeSet<_>>();
|
||||
for imported_config_item in imported_external_models_configs {
|
||||
let (_, system_config) = imported_config_item.into_parts();
|
||||
let exists =
|
||||
existing_system_config_keys.contains(ADMIN_EXTERNAL_MODELS_PROXY_NODE_CONFIG_KEY);
|
||||
match (exists, merge_mode) {
|
||||
(true, AdminImportMergeMode::Skip) => {
|
||||
stats.system_configs.skipped += 1;
|
||||
continue;
|
||||
}
|
||||
(true, AdminImportMergeMode::Error) => {
|
||||
return Ok(Err(invalid_request(format!(
|
||||
"SystemConfig '{ADMIN_EXTERNAL_MODELS_PROXY_NODE_CONFIG_KEY}' 已存在"
|
||||
))));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let imported_proxy_node_id = match system_config.value {
|
||||
Value::Null => None,
|
||||
Value::String(value) => {
|
||||
let value = value.trim();
|
||||
if value.is_empty() {
|
||||
return Ok(Err(invalid_request(
|
||||
"external_models_proxy_node_id 不能为空",
|
||||
)));
|
||||
}
|
||||
Some(value.to_string())
|
||||
}
|
||||
_ => {
|
||||
return Ok(Err(invalid_request(
|
||||
"external_models_proxy_node_id 必须是字符串或 null",
|
||||
)))
|
||||
}
|
||||
};
|
||||
let request_bytes = Bytes::from(
|
||||
serde_json::to_vec(&json!({ "proxy_node_id": null }))
|
||||
.map_err(|err| GatewayError::Internal(err.to_string()))?,
|
||||
);
|
||||
match self
|
||||
.apply_admin_external_models_config_update(&request_bytes)
|
||||
.await?
|
||||
{
|
||||
Ok(_) => {
|
||||
if exists {
|
||||
stats.system_configs.updated += 1;
|
||||
} else {
|
||||
stats.system_configs.created += 1;
|
||||
existing_system_config_keys
|
||||
.insert(ADMIN_EXTERNAL_MODELS_PROXY_NODE_CONFIG_KEY.to_string());
|
||||
}
|
||||
if let Some(node_id) = imported_proxy_node_id {
|
||||
stats.errors.push(format!(
|
||||
"外部模型目录代理节点 '{node_id}' 是当前部署的本地引用;代理节点未导入,已切换为直连"
|
||||
));
|
||||
}
|
||||
}
|
||||
Err((status, payload)) => return Ok(Err((status, payload))),
|
||||
}
|
||||
}
|
||||
|
||||
let mut global_models_by_name = self
|
||||
.list_admin_global_models(&AdminGlobalModelListQuery {
|
||||
offset: 0,
|
||||
@@ -2231,15 +2317,14 @@ impl<'a> AdminAppState<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
let mut existing_system_config_keys = self
|
||||
.list_system_config_entries()
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|entry| normalize_admin_system_config_key(&entry.key))
|
||||
.collect::<BTreeSet<_>>();
|
||||
for imported_config_item in imported_system_configs {
|
||||
let (_, system_config) = imported_config_item.into_parts();
|
||||
let normalized_key = normalize_admin_system_config_key(&system_config.key);
|
||||
let ImportedSystemConfig {
|
||||
key,
|
||||
value,
|
||||
description,
|
||||
} = system_config;
|
||||
let normalized_key = normalize_imported_system_config_key(&key);
|
||||
let exists = existing_system_config_keys.contains(&normalized_key);
|
||||
match (exists, merge_mode) {
|
||||
(true, AdminImportMergeMode::Skip) => {
|
||||
@@ -2256,13 +2341,14 @@ impl<'a> AdminAppState<'a> {
|
||||
|
||||
let request_bytes = Bytes::from(
|
||||
serde_json::to_vec(&json!({
|
||||
"value": system_config.value,
|
||||
"description": system_config.description,
|
||||
"value": value,
|
||||
"description": description,
|
||||
}))
|
||||
.map_err(|err| GatewayError::Internal(err.to_string()))?,
|
||||
);
|
||||
match apply_admin_system_config_update(self, &system_config.key, &request_bytes).await?
|
||||
{
|
||||
let update_result =
|
||||
apply_admin_system_config_update(self, &key, &request_bytes).await?;
|
||||
match update_result {
|
||||
Ok(_) => {
|
||||
if exists {
|
||||
stats.system_configs.updated += 1;
|
||||
|
||||
@@ -29,6 +29,13 @@ impl<'a> AdminAppState<'a> {
|
||||
self.app.read_system_config_json_value(key).await
|
||||
}
|
||||
|
||||
pub(crate) async fn read_system_config_json_value_strong(
|
||||
&self,
|
||||
key: &str,
|
||||
) -> Result<Option<serde_json::Value>, GatewayError> {
|
||||
self.app.read_system_config_json_value_strong(key).await
|
||||
}
|
||||
|
||||
pub(crate) async fn list_system_config_entries(
|
||||
&self,
|
||||
) -> Result<Vec<crate::data::state::StoredSystemConfigEntry>, GatewayError> {
|
||||
|
||||
Reference in New Issue
Block a user