mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-09 20:50:20 +08:00
fix(ai-formats): support cyber policy failover and custom tool/audio passthrough
This commit is contained in:
@@ -937,6 +937,7 @@ mod tests {
|
||||
continue_status_codes: [409, 429].into_iter().collect(),
|
||||
success_failover_patterns: Vec::new(),
|
||||
error_stop_patterns: Vec::new(),
|
||||
stop_cyber_policy_errors: false,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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;
|
||||
use crate::provider_key_auth::provider_key_effective_api_formats;
|
||||
use aether_data_contracts::repository::candidates::{
|
||||
RequestCandidateStatus, StoredRequestCandidate,
|
||||
@@ -204,6 +205,7 @@ pub(crate) fn build_admin_provider_summary_value(
|
||||
"ops_configured": ops_configured,
|
||||
"ops_architecture_id": ops_architecture_id,
|
||||
"kiro_simulated_cache_enabled": kiro_simulated_cache_enabled,
|
||||
"codex_cyber_flag_passthrough_enabled": codex_cyber_flag_passthrough_enabled(&provider.provider_type, provider.config.as_ref()),
|
||||
"ops_quota_alert_enabled": ops_quota_alert_enabled,
|
||||
"created_at": endpoint_timestamp_or_now(provider.created_at_unix_ms, now_unix_secs),
|
||||
"updated_at": endpoint_timestamp_or_now(provider.updated_at_unix_secs, now_unix_secs),
|
||||
|
||||
@@ -33,6 +33,7 @@ pub(crate) enum LocalFailoverClassification {
|
||||
StopStatusCode,
|
||||
StopErrorPattern,
|
||||
StopExecutionError,
|
||||
StopCyberPolicy,
|
||||
RetrySuccessPattern,
|
||||
RetryStatusCode,
|
||||
RetryUpstreamFailure,
|
||||
@@ -45,6 +46,7 @@ impl LocalFailoverClassification {
|
||||
Self::StopStatusCode => "stop_status_code",
|
||||
Self::StopErrorPattern => "stop_error_pattern",
|
||||
Self::StopExecutionError => "stop_execution_error",
|
||||
Self::StopCyberPolicy => "stop_cyber_policy",
|
||||
Self::RetrySuccessPattern => "retry_success_pattern",
|
||||
Self::RetryStatusCode => "retry_status_code",
|
||||
Self::RetryUpstreamFailure => "retry_upstream_failure",
|
||||
@@ -60,6 +62,13 @@ pub(crate) fn classify_local_failover(
|
||||
return LocalFailoverClassification::StopStatusCode;
|
||||
}
|
||||
|
||||
if policy.stop_cyber_policy_errors
|
||||
&& input.status_code >= 400
|
||||
&& local_error_response_has_cyber_policy_code(input.response_text)
|
||||
{
|
||||
return LocalFailoverClassification::StopCyberPolicy;
|
||||
}
|
||||
|
||||
if input.status_code >= 400
|
||||
&& policy.error_stop_patterns.iter().any(|rule| {
|
||||
local_failover_regex_rule_matches(rule, input.response_text, input.status_code)
|
||||
@@ -104,6 +113,44 @@ fn should_failover_local_upstream_status(status_code: u16) -> bool {
|
||||
status_code >= 400
|
||||
}
|
||||
|
||||
fn local_error_response_has_cyber_policy_code(response_text: Option<&str>) -> bool {
|
||||
let Some(response_text) = response_text else {
|
||||
return false;
|
||||
};
|
||||
let Ok(value) = serde_json::from_str::<Value>(response_text) else {
|
||||
return false;
|
||||
};
|
||||
json_value_has_cyber_policy_code(&value, 0)
|
||||
}
|
||||
|
||||
fn json_value_has_cyber_policy_code(value: &Value, depth: usize) -> bool {
|
||||
if depth > 16 {
|
||||
return false;
|
||||
}
|
||||
match value {
|
||||
Value::Object(object) => object.iter().any(|(key, value)| {
|
||||
(key == "code"
|
||||
&& value
|
||||
.as_str()
|
||||
.is_some_and(|code| code.eq_ignore_ascii_case("cyber_policy")))
|
||||
|| json_value_has_cyber_policy_code(value, depth + 1)
|
||||
}),
|
||||
Value::Array(values) => values
|
||||
.iter()
|
||||
.any(|value| json_value_has_cyber_policy_code(value, depth + 1)),
|
||||
Value::String(text) => {
|
||||
let text = text.trim_start();
|
||||
if !text.starts_with('{') && !text.starts_with('[') {
|
||||
return false;
|
||||
}
|
||||
serde_json::from_str::<Value>(text)
|
||||
.ok()
|
||||
.is_some_and(|value| json_value_has_cyber_policy_code(&value, depth + 1))
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_local_error_response(response_text: Option<&str>) -> ParsedLocalErrorResponse {
|
||||
let raw = response_text
|
||||
.map(str::trim)
|
||||
@@ -310,6 +357,58 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn classifier_stops_cyber_policy_when_policy_enabled() {
|
||||
let policy = LocalFailoverPolicy {
|
||||
stop_cyber_policy_errors: true,
|
||||
..LocalFailoverPolicy::default()
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
classify_local_failover(
|
||||
&policy,
|
||||
LocalFailoverInput::new(
|
||||
400,
|
||||
Some(
|
||||
r#"{"type":"error","error":{"type":"invalid_request","code":"cyber_policy","message":"flagged"}}"#,
|
||||
)
|
||||
)
|
||||
),
|
||||
LocalFailoverClassification::StopCyberPolicy
|
||||
);
|
||||
assert_eq!(
|
||||
classify_local_failover(
|
||||
&policy,
|
||||
LocalFailoverInput::new(
|
||||
400,
|
||||
Some(r#"{"outer":{"error":{"code":"cyber_policy"}}}"#)
|
||||
)
|
||||
),
|
||||
LocalFailoverClassification::StopCyberPolicy
|
||||
);
|
||||
assert_eq!(
|
||||
classify_local_failover(
|
||||
&policy,
|
||||
LocalFailoverInput::new(400, Some(r#"{"error":{"code":"other"}}"#))
|
||||
),
|
||||
LocalFailoverClassification::RetryUpstreamFailure
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn classifier_retries_cyber_policy_when_policy_disabled() {
|
||||
assert_eq!(
|
||||
classify_local_failover(
|
||||
&LocalFailoverPolicy::default(),
|
||||
LocalFailoverInput::new(
|
||||
400,
|
||||
Some(r#"{"error":{"code":"cyber_policy","message":"flagged"}}"#)
|
||||
)
|
||||
),
|
||||
LocalFailoverClassification::RetryUpstreamFailure
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn classifier_detects_success_continue_status_code() {
|
||||
let policy = LocalFailoverPolicy {
|
||||
|
||||
@@ -903,7 +903,8 @@ fn local_candidate_failure_should_invalidate_affinity(
|
||||
status_code >= 500
|
||||
}
|
||||
LocalFailoverClassification::StopErrorPattern
|
||||
| LocalFailoverClassification::StopExecutionError => false,
|
||||
| LocalFailoverClassification::StopExecutionError
|
||||
| LocalFailoverClassification::StopCyberPolicy => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -332,7 +332,8 @@ fn local_candidate_failure_should_project_health(
|
||||
status_code >= 500
|
||||
}
|
||||
LocalFailoverClassification::StopErrorPattern
|
||||
| LocalFailoverClassification::StopExecutionError => false,
|
||||
| LocalFailoverClassification::StopExecutionError
|
||||
| LocalFailoverClassification::StopCyberPolicy => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,9 +38,9 @@ pub(crate) use self::health::{
|
||||
project_local_key_circuit_failure, project_local_success_health,
|
||||
};
|
||||
pub(crate) use self::policy::{
|
||||
append_local_failover_policy_to_value, local_failover_policy_from_report_context,
|
||||
local_failover_policy_from_transport, resolve_local_failover_policy, LocalFailoverPolicy,
|
||||
LocalFailoverRegexRule,
|
||||
append_local_failover_policy_to_value, codex_cyber_flag_passthrough_enabled,
|
||||
local_failover_policy_from_report_context, local_failover_policy_from_transport,
|
||||
resolve_local_failover_policy, LocalFailoverPolicy, LocalFailoverRegexRule,
|
||||
};
|
||||
pub(crate) use self::recovery::{
|
||||
analyze_local_failover, recover_local_failover_decision, LocalFailoverAnalysis,
|
||||
@@ -95,6 +95,7 @@ pub(crate) fn build_local_error_flow_metadata(
|
||||
LocalFailoverClassification::StopStatusCode
|
||||
| LocalFailoverClassification::StopErrorPattern
|
||||
| LocalFailoverClassification::StopExecutionError
|
||||
| LocalFailoverClassification::StopCyberPolicy
|
||||
);
|
||||
let propagation = match analysis.decision {
|
||||
LocalFailoverDecision::RetryNextCandidate => "suppressed",
|
||||
|
||||
@@ -14,6 +14,7 @@ pub(crate) struct LocalFailoverPolicy {
|
||||
pub(crate) continue_status_codes: BTreeSet<u16>,
|
||||
pub(crate) success_failover_patterns: Vec<LocalFailoverRegexRule>,
|
||||
pub(crate) error_stop_patterns: Vec<LocalFailoverRegexRule>,
|
||||
pub(crate) stop_cyber_policy_errors: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
@@ -80,6 +81,10 @@ pub(crate) fn local_failover_policy_from_transport(
|
||||
|
||||
LocalFailoverPolicy {
|
||||
max_retries,
|
||||
stop_cyber_policy_errors: codex_cyber_flag_passthrough_enabled(
|
||||
&transport.provider.provider_type,
|
||||
transport.provider.config.as_ref(),
|
||||
),
|
||||
stop_status_codes: rules
|
||||
.map(|value| {
|
||||
parse_status_code_set(
|
||||
@@ -135,6 +140,10 @@ pub(crate) fn local_failover_policy_from_report_context(
|
||||
.unwrap_or_default(),
|
||||
success_failover_patterns: parse_regex_rules(object, "success_failover_patterns"),
|
||||
error_stop_patterns: parse_regex_rules(object, "error_stop_patterns"),
|
||||
stop_cyber_policy_errors: object
|
||||
.get("stop_cyber_policy_errors")
|
||||
.and_then(Value::as_bool)
|
||||
.unwrap_or(false),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -168,9 +177,29 @@ fn local_failover_policy_to_value(policy: &LocalFailoverPolicy) -> Value {
|
||||
"continue_status_codes": policy.continue_status_codes.iter().copied().collect::<Vec<_>>(),
|
||||
"success_failover_patterns": policy.success_failover_patterns.iter().map(local_failover_regex_rule_to_value).collect::<Vec<_>>(),
|
||||
"error_stop_patterns": policy.error_stop_patterns.iter().map(local_failover_regex_rule_to_value).collect::<Vec<_>>(),
|
||||
"stop_cyber_policy_errors": policy.stop_cyber_policy_errors,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn codex_cyber_flag_passthrough_enabled(
|
||||
provider_type: &str,
|
||||
provider_config: Option<&Value>,
|
||||
) -> bool {
|
||||
if !provider_type.trim().eq_ignore_ascii_case("codex") {
|
||||
return false;
|
||||
}
|
||||
provider_config
|
||||
.and_then(|config| config.get("codex"))
|
||||
.and_then(Value::as_object)
|
||||
.and_then(|codex| {
|
||||
codex
|
||||
.get("pass_through_cyber_flag_interrupt")
|
||||
.or_else(|| codex.get("passthrough_cyber_flag_interrupt"))
|
||||
.and_then(Value::as_bool)
|
||||
})
|
||||
.unwrap_or(true)
|
||||
}
|
||||
|
||||
fn local_failover_regex_rule_to_value(rule: &LocalFailoverRegexRule) -> Value {
|
||||
json!({
|
||||
"pattern": rule.pattern,
|
||||
@@ -345,7 +374,28 @@ mod tests {
|
||||
pattern: "validation".to_string(),
|
||||
status_codes: [422].into_iter().collect(),
|
||||
}],
|
||||
stop_cyber_policy_errors: false,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn codex_cyber_policy_passthrough_defaults_on_and_can_be_disabled() {
|
||||
let mut transport = sample_transport(None, None, None);
|
||||
transport.provider.provider_type = "codex".to_string();
|
||||
assert!(local_failover_policy_from_transport(&transport).stop_cyber_policy_errors);
|
||||
|
||||
transport.provider.config = Some(json!({
|
||||
"codex": {"pass_through_cyber_flag_interrupt": false}
|
||||
}));
|
||||
assert!(!local_failover_policy_from_transport(&transport).stop_cyber_policy_errors);
|
||||
|
||||
transport.provider.config = Some(json!({
|
||||
"codex": {"passthrough_cyber_flag_interrupt": true}
|
||||
}));
|
||||
assert!(local_failover_policy_from_transport(&transport).stop_cyber_policy_errors);
|
||||
|
||||
transport.provider.provider_type = "llm".to_string();
|
||||
assert!(!local_failover_policy_from_transport(&transport).stop_cyber_policy_errors);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,9 +58,8 @@ const fn decision_from_classification(
|
||||
LocalFailoverClassification::UseDefault => LocalFailoverDecision::UseDefault,
|
||||
LocalFailoverClassification::StopStatusCode
|
||||
| LocalFailoverClassification::StopErrorPattern
|
||||
| LocalFailoverClassification::StopExecutionError => {
|
||||
LocalFailoverDecision::StopLocalFailover
|
||||
}
|
||||
| LocalFailoverClassification::StopExecutionError
|
||||
| LocalFailoverClassification::StopCyberPolicy => LocalFailoverDecision::StopLocalFailover,
|
||||
LocalFailoverClassification::RetrySuccessPattern
|
||||
| LocalFailoverClassification::RetryStatusCode
|
||||
| LocalFailoverClassification::RetryUpstreamFailure => {
|
||||
@@ -144,4 +143,22 @@ mod tests {
|
||||
LocalFailoverClassification::RetryUpstreamFailure
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recovery_stops_cyber_policy_failover() {
|
||||
let policy = LocalFailoverPolicy {
|
||||
stop_cyber_policy_errors: true,
|
||||
..LocalFailoverPolicy::default()
|
||||
};
|
||||
let analysis = analyze_local_failover(
|
||||
&policy,
|
||||
LocalFailoverInput::new(400, Some(r#"{"error":{"code":"cyber_policy"}}"#)),
|
||||
);
|
||||
|
||||
assert_eq!(analysis.decision, LocalFailoverDecision::StopLocalFailover);
|
||||
assert_eq!(
|
||||
analysis.classification,
|
||||
LocalFailoverClassification::StopCyberPolicy
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user