释放已解决的 DirectThread 请求事件

请求无 ID 时立即可清理,解决事件到达后解除对应 requested 事件 pin。
This commit is contained in:
2026-09-15 20:04:34 +08:00
parent dfe6490907
commit d7c7089fa6
@@ -152,6 +152,12 @@ impl DirectThreadManager {
cleanable,
});
Self::mark_item_events_cleanable(thread, event.item_id.as_deref());
if matches!(
event.event_type.as_str(),
"approval.resolved" | "request.resolved" | "ask.resolved"
) {
Self::mark_request_events_cleanable(thread, request_id(&event).as_deref());
}
self.evict(thread_id);
event
}
@@ -266,10 +272,11 @@ impl DirectThreadManager {
true
}
"approval.requested" | "request.requested" | "ask.requested" => {
if let Some(request_id) = request_id(event) {
thread.unresolved_requests.insert(request_id);
let request_id = request_id(event);
if let Some(request_id) = request_id.as_deref() {
thread.unresolved_requests.insert(request_id.to_string());
}
false
request_id.is_none()
}
"approval.resolved" | "request.resolved" | "ask.resolved" => {
if let Some(request_id) = request_id(event) {
@@ -316,6 +323,21 @@ impl DirectThreadManager {
}
}
fn mark_request_events_cleanable(thread: &mut ThreadState, resolved_request_id: Option<&str>) {
let Some(resolved_request_id) = resolved_request_id else {
return;
};
for stored in &mut thread.events {
if matches!(
stored.event.event_type.as_str(),
"approval.requested" | "request.requested" | "ask.requested"
) && request_id(&stored.event).as_deref() == Some(resolved_request_id)
{
stored.cleanable = true;
}
}
}
fn trim_prefix(thread: &mut ThreadState) {
let min_cursor = thread
.subscribers
@@ -583,6 +605,34 @@ mod tests {
);
}
#[test]
fn resolved_request_releases_the_original_requested_event() {
let mut manager = DirectThreadManager::with_limits(100, 100_000);
manager.append(
"thread-1",
DirectThreadRawEventDraft {
event_type: "approval.requested".to_string(),
turn_id: "turn-1".to_string(),
item_id: None,
payload: serde_json::json!({"requestId": "request-1"}),
},
);
let subscription = manager.subscribe("thread-1");
manager.append(
"thread-1",
DirectThreadRawEventDraft {
event_type: "approval.resolved".to_string(),
turn_id: "turn-1".to_string(),
item_id: None,
payload: serde_json::json!({"requestId": "request-1"}),
},
);
manager
.consume(&subscription.subscription_id)
.expect("consume resolution");
assert_eq!(manager.thread_debug("thread-1").unwrap().0, 0);
}
#[test]
fn turn_completed_anchor_survives_empty_queue_for_new_subscriber() {
let mut manager = DirectThreadManager::with_limits(100, 100_000);