- Optimize Codex Chat cache stability

- Stop deriving Codex session/cache identity from `previous_response_id`.
  - Canonicalize parseable JSON string payloads in Chat and Responses tool conversions.
  - Add regression coverage for cache-sensitive conversion behavior.
This commit is contained in:
Jason
2026-05-19 20:30:39 +08:00
parent 74acf1e387
commit 22fbe6f11a
4 changed files with 148 additions and 26 deletions
+9 -19
View File
@@ -6,7 +6,7 @@
//!
//! 支持从客户端请求中提取 Session ID,用于关联同一对话的多个请求:
//! - Claude: 从 `metadata.user_id` (格式: `user_xxx_session_yyy`) 或 `metadata.session_id` 提取
//! - Codex: 从 `previous_response_id` 或 headers 中的 `session_id` 提取
//! - Codex: 从 headers 中的 `session_id` / `x-session-id` 或 `metadata.session_id` 提取
//! - 其他: 生成新的 UUID
use axum::http::HeaderMap;
@@ -197,8 +197,6 @@ pub enum SessionIdSource {
MetadataSessionId,
/// 从 headers 提取 (Codex)
Header,
/// 从 previous_response_id 提取 (Codex)
PreviousResponseId,
/// 新生成
Generated,
}
@@ -228,8 +226,7 @@ pub struct SessionIdResult {
/// ### Codex 请求
/// 1. Headers: `session_id` 或 `x-session-id`
/// 2. `metadata.session_id`
/// 3. `previous_response_id` (对话延续)
/// 4. 生成新 UUID
/// 3. 生成新 UUID
///
/// ## 示例
///
@@ -319,16 +316,9 @@ fn extract_codex_session(headers: &HeaderMap, body: &serde_json::Value) -> Optio
}
}
// 3. 从 previous_response_id 提取(对话延续)
if let Some(prev_id) = body.get("previous_response_id").and_then(|v| v.as_str()) {
if prev_id.len() > 10 {
return Some(SessionIdResult {
session_id: format!("codex_{prev_id}"),
source: SessionIdSource::PreviousResponseId,
client_provided: true,
});
}
}
// previous_response_id 是 Responses 协议里的响应游标,不是稳定会话身份。
// Chat/Responses 桥接时该值通常来自上游每轮返回的随机 response id
// 若把它当 prompt_cache_key 或 Codex session header,会导致每轮请求换缓存 key。
None
}
@@ -585,7 +575,7 @@ mod tests {
}
#[test]
fn test_extract_session_from_codex_previous_response_id() {
fn test_codex_previous_response_id_is_not_stable_session_identity() {
let headers = HeaderMap::new();
let body = json!({
"input": "Write a function",
@@ -594,9 +584,9 @@ mod tests {
let result = extract_session_id(&headers, &body, "codex");
assert_eq!(result.session_id, "codex_resp_abc123def456789");
assert_eq!(result.source, SessionIdSource::PreviousResponseId);
assert!(result.client_provided);
assert!(!result.session_id.is_empty());
assert_eq!(result.source, SessionIdSource::Generated);
assert!(!result.client_provided);
}
#[test]