Class ChatTurnPersistence

java.lang.Object
com.axelor.ai.chat.ChatTurnPersistence

@Singleton public class ChatTurnPersistence extends Object
Owns AI chat persistence: loads history, saves the user row, progressively upserts the assistant row as the agent loop runs, and finalizes on stream end. The caller (the streamer) holds the Turn on the stack and passes it to each method.
  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Class
    Description
    static final record 
    Result of preparing a stream: a Turn to thread through the frames plus a possibly modified request (with history preloaded, the user message saved, or — for sub-agent resume — an isolated child context).
  • Method Summary

    Modifier and Type
    Method
    Description
    void
    append(Turn turn, ChatRequest request, UIMessage message)
    Called for each ASSISTANT / TOOL UIMessage produced by the streamer.
    begin(ChatRequest request)
    Called at stream() start.
    void
    end(Turn turn, ChatRequest request)
    Called at stream end (onComplete or onError).
    void
    Public expiry touchpoint for read paths (the /messages endpoint — which also covers client attach/replay, since attaching always refetches messages first).
    static String
    findConsultCallId(List<UIPart> parts, String subAgentCode)
    Walks parts for the most recent consult_agent tool whose input.agent matches subAgentCode and returns its toolCallId — used to re-attach updates from a sub-agent resume or cascade to the same tool without a DB lookup.
    static List<UIMessage>
    isolateAgentView(List<UIPart> parts, String subAgentCode)
    Builds the sub-agent's isolated view from a single row's parts: a synthetic USER (the consult question pulled from consult_agent's input) plus an ASSISTANT carrying the sub-agent's own parts with the axelor marker stripped.
    void
    keep(Turn turn, ChatRequest request, UIPart part)
    Keeps a part the stream itself adds to the turn, such as a refusal's: it marks the current step, not a step of its own.
    void
    Records the terminal FinishReason observed on the stream.
    The UIMessage view of a persisted row, with server-derived author/agentCode metadata.

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Method Details

    • begin

      public ChatTurnPersistence.Prepared begin(ChatRequest request)
      Called at stream() start. Loads or creates the chat, saves the user row when applicable, preloads the turn from the latest ASSISTANT row on continuation, and returns a fresh or preloaded Turn. Returns new Prepared(request, new Turn()) in the few edge cases where persistence is not relevant (programmatic / null id / empty messages / no user).
    • append

      public void append(Turn turn, ChatRequest request, UIMessage message)
      Called for each ASSISTANT / TOOL UIMessage produced by the streamer. Appends or merges into the turn's parts and progressively upserts the row. Parts produced under a sub-agent (request.isSubAgent()) get stamped with providerMetadata.axelor.agentCode so the provider adapter can filter them out of LLM replay while the wire shape stays AI-SDK valid.
    • keep

      public void keep(Turn turn, ChatRequest request, UIPart part)
      Keeps a part the stream itself adds to the turn, such as a refusal's: it marks the current step, not a step of its own.
    • recordFinish

      public void recordFinish(Turn turn, FinishReason reason)
      Records the terminal FinishReason observed on the stream.
    • end

      public void end(Turn turn, ChatRequest request)
      Called at stream end (onComplete or onError). Sanitizes any still-pending tool parts unless the stream paused legitimately with TOOL_CALLS — even on a client disconnect, a persisted approval/client-tool pause must survive so the next request can resume it.
    • expireStalePendingCalls

      public void expireStalePendingCalls(String chatUuid)
      Public expiry touchpoint for read paths (the /messages endpoint — which also covers client attach/replay, since attaching always refetches messages first). Persists the injected errors so a freshly attached client sees the call as resolved-with-error and nobody re-runs it.
    • toUIMessage

      public UIMessage toUIMessage(AiChatMessage msg)
      The UIMessage view of a persisted row, with server-derived author/agentCode metadata.
    • findConsultCallId

      public static String findConsultCallId(List<UIPart> parts, String subAgentCode)
      Walks parts for the most recent consult_agent tool whose input.agent matches subAgentCode and returns its toolCallId — used to re-attach updates from a sub-agent resume or cascade to the same tool without a DB lookup. Returns null when no such tool is found.
    • isolateAgentView

      public static List<UIMessage> isolateAgentView(List<UIPart> parts, String subAgentCode)
      Builds the sub-agent's isolated view from a single row's parts: a synthetic USER (the consult question pulled from consult_agent's input) plus an ASSISTANT carrying the sub-agent's own parts with the axelor marker stripped. Empty when the row has no matching consult_agent tool.