mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 17:30:23 +08:00
Preserve Python user import compatibility
This commit is contained in:
@@ -516,6 +516,13 @@ fn imported_optional_f64(value: Option<&Value>, field_name: &str) -> Result<Opti
|
|||||||
.filter(|value| value.is_finite())
|
.filter(|value| value.is_finite())
|
||||||
.ok_or_else(|| format!("{field_name} 必须是有限数值"))
|
.ok_or_else(|| format!("{field_name} 必须是有限数值"))
|
||||||
.map(Some),
|
.map(Some),
|
||||||
|
Some(Value::String(value)) => value
|
||||||
|
.trim()
|
||||||
|
.parse::<f64>()
|
||||||
|
.ok()
|
||||||
|
.filter(|value| value.is_finite())
|
||||||
|
.ok_or_else(|| format!("{field_name} 必须是有限数值"))
|
||||||
|
.map(Some),
|
||||||
_ => Err(format!("{field_name} 必须是有限数值")),
|
_ => Err(format!("{field_name} 必须是有限数值")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -542,9 +549,14 @@ fn imported_rfc3339_to_unix_secs(
|
|||||||
let Some(value) = imported_optional_string(value)? else {
|
let Some(value) = imported_optional_string(value)? else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
let parsed = chrono::DateTime::parse_from_rfc3339(&value)
|
let parsed_timestamp = chrono::DateTime::parse_from_rfc3339(&value)
|
||||||
|
.map(|parsed| parsed.timestamp())
|
||||||
|
.or_else(|_| {
|
||||||
|
chrono::NaiveDateTime::parse_from_str(&value, "%Y-%m-%dT%H:%M:%S%.f")
|
||||||
|
.map(|parsed| parsed.and_utc().timestamp())
|
||||||
|
})
|
||||||
.map_err(|_| format!("{field_name} 必须是 RFC3339 时间"))?;
|
.map_err(|_| format!("{field_name} 必须是 RFC3339 时间"))?;
|
||||||
Ok(Some(parsed.timestamp().max(0) as u64))
|
Ok(Some(parsed_timestamp.max(0) as u64))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn imported_string_list_from_value(
|
fn imported_string_list_from_value(
|
||||||
@@ -609,12 +621,11 @@ fn normalize_imported_wallet_target(
|
|||||||
} else {
|
} else {
|
||||||
let total_balance =
|
let total_balance =
|
||||||
imported_optional_f64(map.get("balance"), "wallet.balance")?.unwrap_or(0.0);
|
imported_optional_f64(map.get("balance"), "wallet.balance")?.unwrap_or(0.0);
|
||||||
(total_balance - gift_balance).max(0.0)
|
total_balance - gift_balance
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
0.0
|
0.0
|
||||||
}
|
};
|
||||||
.max(0.0);
|
|
||||||
let limit_mode = if let Some(map) = wallet {
|
let limit_mode = if let Some(map) = wallet {
|
||||||
if let Some(mode) = imported_optional_string(map.get("limit_mode"))? {
|
if let Some(mode) = imported_optional_string(map.get("limit_mode"))? {
|
||||||
match mode.to_ascii_lowercase().as_str() {
|
match mode.to_ascii_lowercase().as_str() {
|
||||||
@@ -2435,8 +2446,8 @@ mod tests {
|
|||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
imported_optional_bool, imported_optional_f64, imported_optional_i32,
|
imported_optional_bool, imported_optional_f64, imported_optional_i32,
|
||||||
imported_optional_u64, imported_string_list_from_value,
|
imported_optional_u64, imported_rfc3339_to_unix_secs, imported_string_list_from_value,
|
||||||
validate_imported_system_users_export_version,
|
normalize_imported_wallet_target, validate_imported_system_users_export_version,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -2453,7 +2464,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn import_rejects_legacy_string_scalars() {
|
fn import_handles_legacy_string_scalars() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
imported_optional_bool(Some(&json!("true"))).unwrap_err(),
|
imported_optional_bool(Some(&json!("true"))).unwrap_err(),
|
||||||
"字段必须是布尔值"
|
"字段必须是布尔值"
|
||||||
@@ -2467,11 +2478,63 @@ mod tests {
|
|||||||
"total_requests 必须是非负整数"
|
"total_requests 必须是非负整数"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
imported_optional_f64(Some(&json!("1.25")), "total_cost_usd").unwrap_err(),
|
imported_optional_f64(Some(&json!("1.25000000")), "total_cost_usd").unwrap(),
|
||||||
|
Some(1.25)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
imported_optional_f64(Some(&json!("not-a-number")), "total_cost_usd").unwrap_err(),
|
||||||
"total_cost_usd 必须是有限数值"
|
"total_cost_usd 必须是有限数值"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn import_handles_python_isoformat_timestamps() {
|
||||||
|
assert_eq!(
|
||||||
|
imported_rfc3339_to_unix_secs(Some(&json!("2099-01-01T00:00:00+00:00")), "expires_at")
|
||||||
|
.unwrap(),
|
||||||
|
Some(4_070_908_800)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
imported_rfc3339_to_unix_secs(Some(&json!("2099-01-01T00:00:00")), "expires_at")
|
||||||
|
.unwrap(),
|
||||||
|
Some(4_070_908_800)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
imported_rfc3339_to_unix_secs(Some(&json!("invalid")), "expires_at").unwrap_err(),
|
||||||
|
"expires_at 必须是 RFC3339 时间"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn import_preserves_python_wallet_negative_recharge_balance() {
|
||||||
|
let wallet = json!({
|
||||||
|
"balance": -4.5,
|
||||||
|
"recharge_balance": -5.25,
|
||||||
|
"gift_balance": 0.75,
|
||||||
|
"limit_mode": "finite"
|
||||||
|
});
|
||||||
|
let wallet = wallet.as_object().expect("wallet should be object");
|
||||||
|
|
||||||
|
let target = normalize_imported_wallet_target(Some(wallet), false).unwrap();
|
||||||
|
assert_eq!(target.recharge_balance, -5.25);
|
||||||
|
assert_eq!(target.gift_balance, 0.75);
|
||||||
|
assert_eq!(target.total_recharged, -5.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn import_preserves_python_wallet_negative_balance_fallback() {
|
||||||
|
let wallet = json!({
|
||||||
|
"balance": -4.5,
|
||||||
|
"gift_balance": 0.75,
|
||||||
|
"limit_mode": "finite"
|
||||||
|
});
|
||||||
|
let wallet = wallet.as_object().expect("wallet should be object");
|
||||||
|
|
||||||
|
let target = normalize_imported_wallet_target(Some(wallet), false).unwrap();
|
||||||
|
assert_eq!(target.recharge_balance, -5.25);
|
||||||
|
assert_eq!(target.gift_balance, 0.75);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn import_rejects_legacy_string_lists() {
|
fn import_rejects_legacy_string_lists() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
@@ -614,7 +614,7 @@ async fn gateway_imports_admin_system_users_locally_and_persists_data() {
|
|||||||
"auto_delete_on_expiry": false,
|
"auto_delete_on_expiry": false,
|
||||||
"total_requests": 12,
|
"total_requests": 12,
|
||||||
"total_tokens": 3456,
|
"total_tokens": 3456,
|
||||||
"total_cost_usd": 1.25
|
"total_cost_usd": "1.25000000"
|
||||||
}]
|
}]
|
||||||
}],
|
}],
|
||||||
"standalone_keys": [{
|
"standalone_keys": [{
|
||||||
@@ -630,7 +630,7 @@ async fn gateway_imports_admin_system_users_locally_and_persists_data() {
|
|||||||
"auto_delete_on_expiry": false,
|
"auto_delete_on_expiry": false,
|
||||||
"total_requests": 3,
|
"total_requests": 3,
|
||||||
"total_tokens": 789,
|
"total_tokens": 789,
|
||||||
"total_cost_usd": 0.75,
|
"total_cost_usd": "0.75000000",
|
||||||
"wallet": {
|
"wallet": {
|
||||||
"balance": 30.0,
|
"balance": 30.0,
|
||||||
"recharge_balance": 20.0,
|
"recharge_balance": 20.0,
|
||||||
@@ -713,6 +713,7 @@ async fn gateway_imports_admin_system_users_locally_and_persists_data() {
|
|||||||
assert_eq!(user_api_keys.len(), 1);
|
assert_eq!(user_api_keys.len(), 1);
|
||||||
assert_eq!(user_api_keys[0].name.as_deref(), Some("Alice CLI"));
|
assert_eq!(user_api_keys[0].name.as_deref(), Some("Alice CLI"));
|
||||||
assert_eq!(user_api_keys[0].total_tokens, 3456);
|
assert_eq!(user_api_keys[0].total_tokens, 3456);
|
||||||
|
assert_eq!(user_api_keys[0].total_cost_usd, 1.25);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
user_api_keys[0].allowed_api_formats,
|
user_api_keys[0].allowed_api_formats,
|
||||||
Some(vec!["openai:chat".to_string()])
|
Some(vec!["openai:chat".to_string()])
|
||||||
@@ -739,6 +740,7 @@ async fn gateway_imports_admin_system_users_locally_and_persists_data() {
|
|||||||
Some("Imported Standalone")
|
Some("Imported Standalone")
|
||||||
);
|
);
|
||||||
assert_eq!(standalone_keys[0].total_tokens, 789);
|
assert_eq!(standalone_keys[0].total_tokens, 789);
|
||||||
|
assert_eq!(standalone_keys[0].total_cost_usd, 0.75);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
decrypt_python_fernet_ciphertext(
|
decrypt_python_fernet_ciphertext(
|
||||||
DEVELOPMENT_ENCRYPTION_KEY,
|
DEVELOPMENT_ENCRYPTION_KEY,
|
||||||
|
|||||||
Reference in New Issue
Block a user