mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-04 08:27:46 +08:00
Merge pull request #690 from zhefox/main
fix(provider): classify deleted Codex agent runtime as invalid
This commit is contained in:
@@ -806,6 +806,26 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn oauth_status_filter_matches_deleted_agent_runtime_as_invalid() {
|
||||||
|
let mut key = sample_key("oauth");
|
||||||
|
key.oauth_invalid_at_unix_secs = Some(1_784_728_663);
|
||||||
|
key.oauth_invalid_reason =
|
||||||
|
Some("[REQUEST_FAILED] Agent runtime has been deleted.".to_string());
|
||||||
|
let status_snapshot = provider_key_status_snapshot_payload(&key, "codex");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
admin_pool_oauth_status_filter(
|
||||||
|
&key,
|
||||||
|
"codex",
|
||||||
|
None,
|
||||||
|
status_snapshot.get("oauth").and_then(Value::as_object),
|
||||||
|
1_784_729_000,
|
||||||
|
),
|
||||||
|
Some("invalid")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn codex_cycle_usage_request_uses_actual_monthly_window_boundaries() {
|
fn codex_cycle_usage_request_uses_actual_monthly_window_boundaries() {
|
||||||
let key = sample_key("oauth");
|
let key = sample_key("oauth");
|
||||||
|
|||||||
@@ -319,6 +319,18 @@ fn build_provider_key_oauth_status_snapshot(key: &StoredProviderCatalogKey) -> V
|
|||||||
if let Some(reason) =
|
if let Some(reason) =
|
||||||
tagged_oauth_invalid_reason(invalid_reason.as_deref(), OAUTH_REQUEST_FAILED_PREFIX)
|
tagged_oauth_invalid_reason(invalid_reason.as_deref(), OAUTH_REQUEST_FAILED_PREFIX)
|
||||||
{
|
{
|
||||||
|
if admin_provider_quota_pure::codex_looks_like_token_invalidated(Some(&reason)) {
|
||||||
|
return json!({
|
||||||
|
"code": "invalid",
|
||||||
|
"label": "已失效",
|
||||||
|
"reason": reason,
|
||||||
|
"expires_at": expires_at_unix_secs,
|
||||||
|
"invalid_at": invalid_at_unix_secs,
|
||||||
|
"source": "oauth_invalid",
|
||||||
|
"requires_reauth": true,
|
||||||
|
"expiring_soon": false,
|
||||||
|
});
|
||||||
|
}
|
||||||
return json!({
|
return json!({
|
||||||
"code": "check_failed",
|
"code": "check_failed",
|
||||||
"label": "检查失败",
|
"label": "检查失败",
|
||||||
@@ -4097,6 +4109,26 @@ mod tests {
|
|||||||
assert_eq!(account.get("source"), Some(&json!("oauth_invalid")));
|
assert_eq!(account.get("source"), Some(&json!("oauth_invalid")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn provider_key_status_snapshot_payload_upgrades_deleted_agent_runtime_to_invalid() {
|
||||||
|
let mut key = sample_catalog_key();
|
||||||
|
key.auth_type = "oauth".to_string();
|
||||||
|
key.oauth_invalid_at_unix_secs = Some(1_784_728_663);
|
||||||
|
key.oauth_invalid_reason =
|
||||||
|
Some("[REQUEST_FAILED] Agent runtime has been deleted.".to_string());
|
||||||
|
|
||||||
|
let payload = provider_key_status_snapshot_payload(&key, "codex");
|
||||||
|
let oauth = payload
|
||||||
|
.get("oauth")
|
||||||
|
.and_then(Value::as_object)
|
||||||
|
.expect("oauth snapshot should be object");
|
||||||
|
|
||||||
|
assert_eq!(oauth.get("code"), Some(&json!("invalid")));
|
||||||
|
assert_eq!(oauth.get("label"), Some(&json!("已失效")));
|
||||||
|
assert_eq!(oauth.get("invalid_at"), Some(&json!(1_784_728_663u64)));
|
||||||
|
assert_eq!(oauth.get("requires_reauth"), Some(&json!(true)));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn provider_key_status_snapshot_payload_backfills_workspace_deactivated_from_metadata() {
|
fn provider_key_status_snapshot_payload_backfills_workspace_deactivated_from_metadata() {
|
||||||
let mut key = sample_catalog_key();
|
let mut key = sample_catalog_key();
|
||||||
|
|||||||
@@ -530,6 +530,18 @@ fn build_oauth_status_snapshot_value(key: &StoredProviderCatalogKey) -> Value {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if let Some(reason) = tagged_reason(invalid_reason.as_deref(), OAUTH_REQUEST_FAILED_PREFIX) {
|
if let Some(reason) = tagged_reason(invalid_reason.as_deref(), OAUTH_REQUEST_FAILED_PREFIX) {
|
||||||
|
if admin_provider_quota_pure::codex_looks_like_token_invalidated(Some(&reason)) {
|
||||||
|
return json!({
|
||||||
|
"code": "invalid",
|
||||||
|
"label": "已失效",
|
||||||
|
"reason": reason,
|
||||||
|
"expires_at": expires_at_unix_secs,
|
||||||
|
"invalid_at": invalid_at_unix_secs,
|
||||||
|
"source": "oauth_invalid",
|
||||||
|
"requires_reauth": true,
|
||||||
|
"expiring_soon": false,
|
||||||
|
});
|
||||||
|
}
|
||||||
return json!({
|
return json!({
|
||||||
"code": "check_failed",
|
"code": "check_failed",
|
||||||
"label": "检查失败",
|
"label": "检查失败",
|
||||||
|
|||||||
@@ -1311,6 +1311,7 @@ pub fn codex_looks_like_token_invalidated(message: Option<&str>) -> bool {
|
|||||||
|| lowered.contains("authentication token has been invalidated")
|
|| lowered.contains("authentication token has been invalidated")
|
||||||
|| lowered.contains("token has been invalidated")
|
|| lowered.contains("token has been invalidated")
|
||||||
|| lowered.contains("token invalidated")
|
|| lowered.contains("token invalidated")
|
||||||
|
|| lowered.contains("agent runtime has been deleted")
|
||||||
|| lowered.contains("personal access token owner is inactive")
|
|| lowered.contains("personal access token owner is inactive")
|
||||||
|| lowered.contains("biscuit_baker_service_auth_credential_error_status")
|
|| lowered.contains("biscuit_baker_service_auth_credential_error_status")
|
||||||
|| lowered.contains("auth_credential")
|
|| lowered.contains("auth_credential")
|
||||||
@@ -2223,6 +2224,16 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn codex_runtime_invalid_reason_marks_deleted_agent_runtime_as_invalid() {
|
||||||
|
assert_eq!(
|
||||||
|
codex_runtime_invalid_reason(403, Some("Agent runtime has been deleted.")),
|
||||||
|
Some(format!(
|
||||||
|
"{OAUTH_EXPIRED_PREFIX}Agent runtime has been deleted."
|
||||||
|
))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn codex_runtime_invalid_reason_marks_402_as_account_blocked() {
|
fn codex_runtime_invalid_reason_marks_402_as_account_blocked() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
@@ -451,6 +451,16 @@ fn resolve_from_oauth_invalid_reason(reason: Option<&str>) -> Option<PoolAccount
|
|||||||
} else {
|
} else {
|
||||||
cleaned
|
cleaned
|
||||||
};
|
};
|
||||||
|
if oauth_request_failure_indicates_invalid_identity(&reason) {
|
||||||
|
return Some(PoolAccountState {
|
||||||
|
blocked: true,
|
||||||
|
code: Some("oauth_token_invalid".to_string()),
|
||||||
|
label: Some("Token 失效".to_string()),
|
||||||
|
reason: Some(reason),
|
||||||
|
source: Some("oauth_invalid".to_string()),
|
||||||
|
recoverable: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
return Some(PoolAccountState {
|
return Some(PoolAccountState {
|
||||||
blocked: false,
|
blocked: false,
|
||||||
code: Some("oauth_request_failed".to_string()),
|
code: Some("oauth_request_failed".to_string()),
|
||||||
@@ -485,6 +495,13 @@ fn resolve_from_oauth_invalid_reason(reason: Option<&str>) -> Option<PoolAccount
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn oauth_request_failure_indicates_invalid_identity(reason: &str) -> bool {
|
||||||
|
reason
|
||||||
|
.trim()
|
||||||
|
.to_ascii_lowercase()
|
||||||
|
.contains("agent runtime has been deleted")
|
||||||
|
}
|
||||||
|
|
||||||
pub fn resolve_pool_account_state(
|
pub fn resolve_pool_account_state(
|
||||||
provider_type: Option<&str>,
|
provider_type: Option<&str>,
|
||||||
upstream_metadata: Option<&Value>,
|
upstream_metadata: Option<&Value>,
|
||||||
@@ -551,6 +568,24 @@ pub fn resolve_account_status_snapshot(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(cleaned) = tagged_reason(&text, "REQUEST_FAILED") {
|
||||||
|
let reason = if cleaned.is_empty() {
|
||||||
|
"账号状态检查失败".to_string()
|
||||||
|
} else {
|
||||||
|
cleaned
|
||||||
|
};
|
||||||
|
if oauth_request_failure_indicates_invalid_identity(&reason) {
|
||||||
|
return AccountStatusSnapshot {
|
||||||
|
code: "oauth_token_invalid".to_string(),
|
||||||
|
label: Some("Token 失效".to_string()),
|
||||||
|
reason: Some(reason),
|
||||||
|
blocked: true,
|
||||||
|
source: Some("oauth_invalid".to_string()),
|
||||||
|
recoverable: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if tagged_reason(&text, "REFRESH_FAILED").is_some() {
|
if tagged_reason(&text, "REFRESH_FAILED").is_some() {
|
||||||
return AccountStatusSnapshot::default();
|
return AccountStatusSnapshot::default();
|
||||||
}
|
}
|
||||||
@@ -739,6 +774,34 @@ mod tests {
|
|||||||
assert!(!snapshot.recoverable);
|
assert!(!snapshot.recoverable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deleted_agent_runtime_request_failure_is_hard_invalid() {
|
||||||
|
let reason = "[REQUEST_FAILED] Agent runtime has been deleted.";
|
||||||
|
let state = resolve_pool_account_state(Some("codex"), None, Some(reason));
|
||||||
|
let snapshot = resolve_account_status_snapshot(Some("codex"), None, Some(reason));
|
||||||
|
|
||||||
|
assert!(state.blocked);
|
||||||
|
assert_eq!(state.code.as_deref(), Some("oauth_token_invalid"));
|
||||||
|
assert!(!state.recoverable);
|
||||||
|
assert_eq!(snapshot.code, "oauth_token_invalid");
|
||||||
|
assert_eq!(snapshot.label.as_deref(), Some("Token 失效"));
|
||||||
|
assert!(snapshot.blocked);
|
||||||
|
assert!(!snapshot.recoverable);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn transient_request_failure_remains_recoverable() {
|
||||||
|
let state = resolve_pool_account_state(
|
||||||
|
Some("codex"),
|
||||||
|
None,
|
||||||
|
Some("[REQUEST_FAILED] upstream request timed out"),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(!state.blocked);
|
||||||
|
assert_eq!(state.code.as_deref(), Some("oauth_request_failed"));
|
||||||
|
assert!(state.recoverable);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn account_snapshot_marks_oauth_expired_as_token_expired() {
|
fn account_snapshot_marks_oauth_expired_as_token_expired() {
|
||||||
let snapshot = resolve_account_status_snapshot(
|
let snapshot = resolve_account_status_snapshot(
|
||||||
|
|||||||
Reference in New Issue
Block a user