Skip to main content

agentic_core/executor/
persist.rs

1//! Step 3 of the conversation pipeline — response persistence.
2//!
3//! Writes the completed response and output items to storage, routing to the
4//! appropriate handler based on whether the turn belongs to a conversation.
5
6use crate::executor::error::ExecutorResult;
7use crate::executor::modes::{ConversationHandler, ResponseHandler};
8use crate::executor::request::RequestContext;
9use crate::types::event::ResponseStatus;
10use crate::types::request_response::ResponsePayload;
11
12#[must_use]
13pub(crate) fn should_persist(ctx: &RequestContext) -> bool {
14    ctx.original_request.store
15        || ctx.original_request.previous_response_id.is_some()
16        || ctx.original_request.conversation_id.is_some()
17}
18
19pub(crate) async fn persist_if_needed(
20    payload: ResponsePayload,
21    ctx: RequestContext,
22    conv_handler: ConversationHandler,
23    resp_handler: ResponseHandler,
24) -> ExecutorResult<()> {
25    if should_persist(&ctx) {
26        persist_response(payload, ctx, conv_handler, resp_handler).await
27    } else {
28        Ok(())
29    }
30}
31
32/// Step 3 — Persist the completed response to storage.
33///
34/// Skipped if [`ResponseStatus`] is not `Completed`/`Incomplete` or `payload.id` is empty.
35/// Routes to [`ConversationHandler`] when `ctx.conversation_id` is set,
36/// otherwise [`ResponseHandler`].
37///
38/// # Errors
39/// Returns [`ExecutorError`] if the storage operation fails.
40pub async fn persist_response(
41    payload: ResponsePayload,
42    ctx: RequestContext,
43    conv_handler: ConversationHandler,
44    resp_handler: ResponseHandler,
45) -> ExecutorResult<()> {
46    // Use typed enum — no hardcoded status strings.
47    if !matches!(
48        payload.status.parse::<ResponseStatus>().unwrap_or_default(),
49        ResponseStatus::Completed | ResponseStatus::Incomplete
50    ) || payload.id.is_empty()
51    {
52        return Ok(());
53    }
54
55    // Move output items from payload; handlers build ResponseMetadata from ctx internally.
56    let output_items = payload.output;
57
58    if ctx.conversation_id.is_some() {
59        conv_handler.execute_turn(ctx, output_items).await
60    } else {
61        resp_handler.execute_turn(ctx, output_items).await
62    }
63}