mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-11 11:49:50 +08:00
feat(codex): stabilize identity across retries
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
//! Client-side Responses WebSocket event forwarding and follow-up planning.
|
||||
|
||||
use aether_provider_transport::CodexFingerprintConvergenceContext;
|
||||
use axum::extract::ws::{Message as AxumWsMessage, WebSocket};
|
||||
use futures_util::SinkExt;
|
||||
use serde_json::Value;
|
||||
@@ -248,7 +249,14 @@ pub(super) async fn forward_client_message(
|
||||
// derive one strong live control snapshot that every stage below
|
||||
// shares. The connection's Upgrade-time decision is only the
|
||||
// immutable identity seed.
|
||||
let planning_parts = build_planning_parts(context);
|
||||
let logical_turn_id = Uuid::now_v7().to_string();
|
||||
let mut planning_parts = build_planning_parts(context);
|
||||
let codex_fingerprint_context =
|
||||
crate::ai_serving::codex_context::attach_codex_logical_turn_context(
|
||||
&mut planning_parts,
|
||||
&client_event,
|
||||
&logical_turn_id,
|
||||
);
|
||||
let turn_control = match resolve_responses_websocket_turn_control(
|
||||
state,
|
||||
context,
|
||||
@@ -453,6 +461,8 @@ pub(super) async fn forward_client_message(
|
||||
context,
|
||||
planning_parts,
|
||||
client_event,
|
||||
logical_turn_id,
|
||||
codex_fingerprint_context,
|
||||
turn_control,
|
||||
turn_redaction_session,
|
||||
)
|
||||
@@ -466,6 +476,8 @@ pub(super) async fn forward_client_message(
|
||||
planning_parts,
|
||||
client_event,
|
||||
requested_model,
|
||||
logical_turn_id,
|
||||
codex_fingerprint_context,
|
||||
turn_control,
|
||||
raw_responses_lite_static_config
|
||||
.expect("independent turns always retain their raw static config"),
|
||||
@@ -513,6 +525,8 @@ async fn forward_pinned_continuation(
|
||||
context: &WebSocketRequestContext,
|
||||
planning_parts: http::request::Parts,
|
||||
client_event: Value,
|
||||
logical_turn_id: String,
|
||||
codex_fingerprint_context: CodexFingerprintConvergenceContext,
|
||||
turn_control: ResponsesWebSocketTurnControl,
|
||||
turn_redaction_session: Option<RedactionSession>,
|
||||
) -> RelayDisposition {
|
||||
@@ -556,7 +570,6 @@ async fn forward_pinned_continuation(
|
||||
};
|
||||
|
||||
let turn_request_id = Uuid::new_v4().to_string();
|
||||
let logical_turn_id = Uuid::new_v4().to_string();
|
||||
let planned = match await_owned_responses_websocket_plan(spawn_owned_responses_websocket_plan(
|
||||
state.clone(),
|
||||
planning_parts,
|
||||
@@ -766,6 +779,7 @@ async fn forward_pinned_continuation(
|
||||
bound.body_normalization = normalization;
|
||||
bound.turn_state.begin(
|
||||
LogicalTurn::new(client_event, turn_index, logical_turn_id)
|
||||
.with_codex_fingerprint_context(codex_fingerprint_context)
|
||||
.with_provider_store(provider_event.get("store") == Some(&Value::Bool(true)))
|
||||
.with_turn_control(turn_control),
|
||||
turn,
|
||||
@@ -800,12 +814,13 @@ async fn forward_replanned_response_create(
|
||||
planning_parts: http::request::Parts,
|
||||
client_event: Value,
|
||||
requested_model: String,
|
||||
logical_turn_id: String,
|
||||
codex_fingerprint_context: CodexFingerprintConvergenceContext,
|
||||
turn_control: ResponsesWebSocketTurnControl,
|
||||
raw_responses_lite_static_config: ResponsesLiteStaticConfig,
|
||||
turn_redaction_session: Option<RedactionSession>,
|
||||
) -> RelayDisposition {
|
||||
let turn_request_id = Uuid::new_v4().to_string();
|
||||
let logical_turn_id = Uuid::new_v4().to_string();
|
||||
let now_unix_secs = current_unix_secs();
|
||||
let excluded_key_ids = bound.exhausted_exclusions.key_ids(now_unix_secs);
|
||||
let excluded_codex_account_ids = bound.exhausted_exclusions.codex_account_ids(now_unix_secs);
|
||||
@@ -992,6 +1007,7 @@ async fn forward_replanned_response_create(
|
||||
bound.body_normalization = normalization;
|
||||
bound.turn_state.begin(
|
||||
LogicalTurn::new(client_event.clone(), turn_index, logical_turn_id.clone())
|
||||
.with_codex_fingerprint_context(codex_fingerprint_context.clone())
|
||||
.with_provider_store(provider_event.get("store") == Some(&Value::Bool(true)))
|
||||
.with_turn_control(turn_control),
|
||||
turn,
|
||||
@@ -1076,6 +1092,7 @@ async fn forward_replanned_response_create(
|
||||
bound.binding_identity = replacement.binding_identity;
|
||||
bound.turn_state.begin(
|
||||
LogicalTurn::new(client_event, turn_index, logical_turn_id)
|
||||
.with_codex_fingerprint_context(codex_fingerprint_context)
|
||||
.with_provider_store(provider_event.get("store") == Some(&Value::Bool(true)))
|
||||
.with_turn_control(turn_control),
|
||||
turn,
|
||||
|
||||
@@ -146,6 +146,7 @@ pub(super) async fn retry_active_turn_after_quota_exhaustion(
|
||||
};
|
||||
let turn_index = active.turn_index;
|
||||
let logical_turn_id = active.logical_turn_id.clone();
|
||||
let codex_fingerprint_context = active.codex_fingerprint_context.clone();
|
||||
let turn_attempt = active.turn_attempt;
|
||||
|
||||
let retry_exclusion_until_unix_secs = bound
|
||||
@@ -154,7 +155,13 @@ pub(super) async fn retry_active_turn_after_quota_exhaustion(
|
||||
let exhausted_key = record_exhausted_bound_key(bound, retry_exclusion_until_unix_secs);
|
||||
let exhausted_key_id = exhausted_key.as_ref().map(|(key_id, _)| key_id.clone());
|
||||
|
||||
let planning_parts = build_planning_parts(context);
|
||||
let mut planning_parts = build_planning_parts(context);
|
||||
if let Some(codex_fingerprint_context) = codex_fingerprint_context.as_ref() {
|
||||
crate::ai_serving::codex_context::restore_codex_logical_turn_context(
|
||||
&mut planning_parts,
|
||||
codex_fingerprint_context,
|
||||
);
|
||||
}
|
||||
let turn_request_id = Uuid::new_v4().to_string();
|
||||
let now_unix_secs = current_unix_secs();
|
||||
let excluded_key_ids = bound.exhausted_exclusions.key_ids(now_unix_secs);
|
||||
|
||||
@@ -444,7 +444,14 @@ async fn bootstrap_responses_websocket(
|
||||
let raw_responses_lite_static_config =
|
||||
ResponsesLiteStaticConfig::from_response_create(&first_event);
|
||||
|
||||
let planning_parts = build_planning_parts(context);
|
||||
let first_logical_turn_id = Uuid::now_v7().to_string();
|
||||
let mut planning_parts = build_planning_parts(context);
|
||||
let first_codex_fingerprint_context =
|
||||
crate::ai_serving::codex_context::attach_codex_logical_turn_context(
|
||||
&mut planning_parts,
|
||||
&first_event,
|
||||
&first_logical_turn_id,
|
||||
);
|
||||
let turn_control = match resolve_responses_websocket_turn_control(
|
||||
&state,
|
||||
context,
|
||||
@@ -860,7 +867,6 @@ async fn bootstrap_responses_websocket(
|
||||
return None;
|
||||
}
|
||||
};
|
||||
let first_logical_turn_id = Uuid::new_v4().to_string();
|
||||
let first_turn_decision = prepare_responses_websocket_turn_decision(
|
||||
&decision,
|
||||
context.trace_id.clone(),
|
||||
@@ -954,6 +960,7 @@ async fn bootstrap_responses_websocket(
|
||||
}
|
||||
bound.turn_state.begin(
|
||||
LogicalTurn::new(first_event, 1, first_logical_turn_id)
|
||||
.with_codex_fingerprint_context(first_codex_fingerprint_context)
|
||||
.with_provider_store(first_provider_event.get("store") == Some(&Value::Bool(true)))
|
||||
.with_turn_control(turn_control),
|
||||
first_turn,
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
//! 非法组合只能靠调用点的 if 和「记得同时改另外两个字段」来避免。这里把它收敛成
|
||||
//! 一个枚举:合法组合由类型保证,转换只能走受控 API。
|
||||
|
||||
use aether_provider_transport::CodexFingerprintConvergenceContext;
|
||||
use serde_json::Value;
|
||||
|
||||
use super::control::ResponsesWebSocketTurnControl;
|
||||
@@ -26,6 +27,9 @@ pub(super) struct LogicalTurn {
|
||||
pub(super) provider_store: bool,
|
||||
pub(super) turn_index: u64,
|
||||
pub(super) logical_turn_id: String,
|
||||
/// Immutable Codex client identity for every provider attempt belonging to
|
||||
/// this logical turn. A transparent re-plan must never mint a new turn.
|
||||
pub(super) codex_fingerprint_context: Option<CodexFingerprintConvergenceContext>,
|
||||
pub(super) turn_attempt: u32,
|
||||
pub(super) retry_attempted: bool,
|
||||
pub(super) retry_unsafe_reason: Option<&'static str>,
|
||||
@@ -42,6 +46,7 @@ impl LogicalTurn {
|
||||
provider_store: false,
|
||||
turn_index,
|
||||
logical_turn_id,
|
||||
codex_fingerprint_context: None,
|
||||
turn_attempt: 1,
|
||||
retry_attempted: false,
|
||||
retry_unsafe_reason: None,
|
||||
@@ -54,6 +59,14 @@ impl LogicalTurn {
|
||||
self
|
||||
}
|
||||
|
||||
pub(super) fn with_codex_fingerprint_context(
|
||||
mut self,
|
||||
context: CodexFingerprintConvergenceContext,
|
||||
) -> Self {
|
||||
self.codex_fingerprint_context = Some(context);
|
||||
self
|
||||
}
|
||||
|
||||
pub(super) fn with_provider_store(mut self, provider_store: bool) -> Self {
|
||||
self.provider_store = provider_store;
|
||||
self
|
||||
|
||||
Reference in New Issue
Block a user