feat(providers): add provider transfer limits

This commit is contained in:
elky
2026-07-26 15:06:56 +08:00
parent 2ef7ac79bc
commit 10d369f59c
36 changed files with 1764 additions and 119 deletions
@@ -157,6 +157,10 @@ pub(crate) struct AdminProviderCreateRequest {
#[serde(default)]
pub(crate) max_retries: Option<i32>,
#[serde(default)]
pub(crate) max_transfer_count: Option<i64>,
#[serde(default)]
pub(crate) max_transfer_timeout_seconds: Option<i64>,
#[serde(default)]
pub(crate) proxy: Option<serde_json::Value>,
#[serde(
default,
@@ -212,6 +216,10 @@ pub(crate) struct AdminProviderUpdateRequest {
#[serde(default)]
pub(crate) max_retries: Option<i32>,
#[serde(default)]
pub(crate) max_transfer_count: Option<i64>,
#[serde(default)]
pub(crate) max_transfer_timeout_seconds: Option<i64>,
#[serde(default)]
pub(crate) proxy: Option<serde_json::Value>,
#[serde(
default,
@@ -12,6 +12,35 @@ pub(crate) const ADMIN_PROVIDER_POOL_QUOTA_PROBE_ACTIVE_SET_PREFIX: &str =
"ap:quota_probe:active_members";
pub(crate) const ADMIN_PROVIDER_OAUTH_DATA_UNAVAILABLE_DETAIL: &str =
"Admin provider OAuth data unavailable";
pub(crate) const PROVIDER_MAX_TRANSFER_COUNT_CONFIG_KEY: &str = "max_transfer_count";
pub(crate) const PROVIDER_MAX_TRANSFER_TIMEOUT_SECONDS_CONFIG_KEY: &str =
"max_transfer_timeout_seconds";
pub(crate) fn normalize_provider_transfer_limit(
value: i64,
field_name: &str,
) -> Result<u64, String> {
u64::try_from(value).map_err(|_| format!("{field_name} 必须是非负整数"))
}
pub(crate) fn normalize_provider_transfer_limit_json(
value: &serde_json::Value,
field_name: &str,
) -> Result<u64, String> {
value
.as_u64()
.ok_or_else(|| format!("{field_name} 必须是非负整数"))
}
pub(crate) fn provider_transfer_limit_from_config(
config: Option<&serde_json::Map<String, serde_json::Value>>,
field_name: &str,
) -> u64 {
config
.and_then(|config| config.get(field_name))
.and_then(serde_json::Value::as_u64)
.unwrap_or(0)
}
pub(crate) fn admin_provider_pool_quota_probe_active_members_key(provider_id: &str) -> String {
format!("{ADMIN_PROVIDER_POOL_QUOTA_PROBE_ACTIVE_SET_PREFIX}:{provider_id}")
@@ -1,3 +1,7 @@
use crate::handlers::admin::provider::shared::support::{
provider_transfer_limit_from_config, PROVIDER_MAX_TRANSFER_COUNT_CONFIG_KEY,
PROVIDER_MAX_TRANSFER_TIMEOUT_SECONDS_CONFIG_KEY,
};
use crate::handlers::admin::shared::unix_secs_to_rfc3339;
use crate::handlers::public::{request_candidate_event_unix_ms, request_candidate_status_label};
use crate::orchestration::codex_cyber_flag_passthrough_enabled;
@@ -126,6 +130,12 @@ pub(crate) fn build_admin_provider_summary_value(
let config = provider_config
.as_ref()
.and_then(serde_json::Value::as_object);
let max_transfer_count =
provider_transfer_limit_from_config(config, PROVIDER_MAX_TRANSFER_COUNT_CONFIG_KEY);
let max_transfer_timeout_seconds = provider_transfer_limit_from_config(
config,
PROVIDER_MAX_TRANSFER_TIMEOUT_SECONDS_CONFIG_KEY,
);
let provider_ops_config = config.and_then(|cfg| cfg.get("provider_ops"));
let ops_configured = provider_ops_config.is_some_and(json_truthy);
let ops_architecture_id = provider_ops_config
@@ -184,6 +194,8 @@ pub(crate) fn build_admin_provider_summary_value(
"quota_last_reset_at": quota_last_reset_at,
"quota_expires_at": quota_expires_at,
"max_retries": provider.max_retries,
"max_transfer_count": max_transfer_count,
"max_transfer_timeout_seconds": max_transfer_timeout_seconds,
"proxy": provider.proxy.clone(),
"stream_first_byte_timeout": provider.stream_first_byte_timeout_secs,
"request_timeout": provider.request_timeout_secs,
@@ -1,6 +1,8 @@
use crate::handlers::admin::provider::shared::payloads::AdminProviderCreateRequest;
use crate::handlers::admin::provider::shared::support::{
normalize_provider_billing_type, parse_optional_rfc3339_unix_secs,
normalize_provider_billing_type, normalize_provider_transfer_limit,
normalize_provider_transfer_limit_json, parse_optional_rfc3339_unix_secs,
PROVIDER_MAX_TRANSFER_COUNT_CONFIG_KEY, PROVIDER_MAX_TRANSFER_TIMEOUT_SECONDS_CONFIG_KEY,
};
use crate::handlers::admin::provider::write::normalize::normalize_chat_pii_redaction_config;
use crate::handlers::admin::provider::write::normalize::normalize_pool_advanced_config;
@@ -114,6 +116,27 @@ pub(crate) async fn build_admin_create_provider_record(
let mut config_map = normalize_json_object(payload.config, "config")?
.and_then(|value| value.as_object().cloned())
.unwrap_or_default();
for (field_name, payload_value) in [
(
PROVIDER_MAX_TRANSFER_COUNT_CONFIG_KEY,
payload.max_transfer_count,
),
(
PROVIDER_MAX_TRANSFER_TIMEOUT_SECONDS_CONFIG_KEY,
payload.max_transfer_timeout_seconds,
),
] {
let value = match payload_value {
Some(value) => Some(normalize_provider_transfer_limit(value, field_name)?),
None => config_map
.get(field_name)
.map(|value| normalize_provider_transfer_limit_json(value, field_name))
.transpose()?,
};
if let Some(value) = value {
config_map.insert(field_name.to_string(), json!(value));
}
}
if let Some(value) = normalize_pool_advanced_config(payload.pool_advanced)? {
config_map.insert("pool_advanced".to_string(), value);
}
@@ -1,6 +1,8 @@
use crate::handlers::admin::provider::shared::payloads::AdminProviderUpdatePatch;
use crate::handlers::admin::provider::shared::support::{
normalize_provider_billing_type, parse_optional_rfc3339_unix_secs,
normalize_provider_billing_type, normalize_provider_transfer_limit,
normalize_provider_transfer_limit_json, parse_optional_rfc3339_unix_secs,
PROVIDER_MAX_TRANSFER_COUNT_CONFIG_KEY, PROVIDER_MAX_TRANSFER_TIMEOUT_SECONDS_CONFIG_KEY,
};
use crate::handlers::admin::provider::write::normalize::normalize_chat_pii_redaction_config;
use crate::handlers::admin::provider::write::normalize::normalize_pool_advanced_config;
@@ -242,6 +244,30 @@ pub(crate) async fn build_admin_update_provider_record(
}
}
for (field_name, payload_value) in [
(
PROVIDER_MAX_TRANSFER_COUNT_CONFIG_KEY,
payload.max_transfer_count,
),
(
PROVIDER_MAX_TRANSFER_TIMEOUT_SECONDS_CONFIG_KEY,
payload.max_transfer_timeout_seconds,
),
] {
if fields.contains(field_name) {
let value = payload_value
.map(|value| normalize_provider_transfer_limit(value, field_name))
.transpose()?
.unwrap_or(0);
config_map.insert(field_name.to_string(), json!(value));
} else if fields.contains("config") {
if let Some(value) = config_map.get(field_name) {
let value = normalize_provider_transfer_limit_json(value, field_name)?;
config_map.insert(field_name.to_string(), json!(value));
}
}
}
if fields.contains("claude_code_advanced") {
if fields.is_null("claude_code_advanced") {
config_map.remove("claude_code_advanced");