Workflow Session Management#
RAGFlow agent workflow sessions persist the complete execution state — DSL, execution path, conversation history, globals, and retrieval results — across API calls. This allows multi-turn interactions and mid-workflow pauses (e.g., waiting for user form input) to survive process boundaries.
Key entities:
| Entity | Location | Role |
|---|---|---|
Canvas | agent/canvas.py | Runtime engine; holds in-memory DSL, path, globals, history |
API4Conversation | api/db/db_models.py | DB row storing dsl, message, reference, errors per session |
API4ConversationService | api/db/services/api_service.py | ORM service for read/write |
completion() | api/db/services/canvas_service.py | Core session lifecycle handler |
_run_workflow_session() | api/apps/restful_apis/agent_api.py | REST-layer session runner |
A session ID is a 32-char hex UUID (via get_uuid()) that also serves as the Canvas task_id .
Session Lifecycle#
New Session#
When no session_id is provided :
- The agent's DSL is fetched from
UserCanvas.dsl. - A new
Canvasis instantiated andcanvas.reset()is called to zero out path, globals, history, and memory. - An
API4Conversationrow is inserted viaAPI4ConversationService.save()with the initial DSL and an empty message list.
The REST create_agent_session endpoint follows the same pattern but also seeds a prologue message from canvas.get_prologue().
Session Resumption#
When a session_id is supplied :
API4ConversationService.get_by_id(session_id)fetches the stored row.Canvas(conv.dsl, ...)is constructed from the persisted DSL — noreset()is called, so the priorpath,globals,history, retrieval, and all component output states are fully restored.
Persistence After a Run#
After canvas.run() completes :
conv.dsl = str(canvas)serializes the entire updated canvas viaGraph.__str__(), which deep-copies the DSL structure, serializes each component's parameter object viaComponentParamBase.__str__(), and converts any non-serializable callables toNone.API4ConversationService.append_message(conv_id, conv)writes the updated row — new messages, references, errors, and the refreshed DSL — back to the database.
The _run_workflow_session() REST path follows an identical pattern via its inner persist_workflow_session() coroutine.
DSL State Structure#
The dsl field is the single source of truth for a session's runtime state. Its top-level keys are :
| Key | Content |
|---|---|
components | Map of component ID → {obj, downstream, upstream} with serialized param/output state |
path | Ordered list of component IDs executed so far; drives resume logic |
history | [(role, content), ...] conversation turns |
globals | sys.* variables (sys.query, sys.user_id, sys.conversation_turns, sys.files, sys.history, sys.date) and env.* variables |
variables | Agent-level variable definitions (type, default value) |
retrieval | Accumulated retrieval chunks and doc aggregations per turn |
memory | (user, assist, summary) memory entries |
task_id | Session/task UUID |
Canvas.load() reads all these keys back into live in-memory fields on construction, so a resumed Canvas object is indistinguishable from one that ran continuously.
Workflow Pausing via UserFillUp#
UserFillUp (agent/component/fillup.py) pauses a workflow mid-execution to collect structured user input (form fields, file uploads) before continuing downstream.
Pause Mechanism#
After each execution batch, the canvas checks whether any UserFillUp component appears in the remaining path :
- All
UserFillUpnodes in the remaining path are invoked to resolve their current field states. Canvas._is_input_field_satisfied()tests each field. Unsatisfied fields (value is Noneor empty) are collected intoanother_inputs.- If any fields are unsatisfied,
self.pathis trimmed to the pending nodes and auser_inputsSSE event is yielded with the field descriptors and optional tips. The generator then returns (not raises), leavingpathin a paused state. - The paused DSL is saved to
API4Conversation.dslexactly as described above, withpath[0]pointing to theUserFillUpnode.
Resume Detection#
At the start of every Canvas._run_impl() call, the is_resume flag is set :
is_resume = bool(self.path) and self.path[0].lower().find("userfillup") >= 0
When is_resume is True :
- The
workflow_startedevent is not re-emitted. - Execution starts at
idx = 0(theUserFillUpnode), not atlen(self.path) - 1. beginis not re-appended to the path.
Input Handling and Loop Safety#
UserFillUp._invoke() receives the inputs dict from canvas.run(**run_kwargs). If inputs is empty — meaning no new user answer was submitted — _clear_form_values() sets every non-optional file field's value to None. This ensures the satisfaction check will pause again rather than silently reusing a stale answer from a previous loop iteration.
When inputs is populated, each value is resolved via _resolve_input_value() (handles file uploads via FileService.get_files(), JSON-parses object-typed fields) and written to component output via set_output().
REST API Surface#
All agent session endpoints are in api/apps/restful_apis/agent_api.py.
| Method | Endpoint | Handler | Notes |
|---|---|---|---|
POST | /agents/{agent_id}/sessions | create_agent_session | Creates session, seeds prologue, returns normalized session object |
GET | /agents/{agent_id}/sessions | list_agent_sessions | Paginated; supports dsl, user_id, keywords, date filters |
GET | /agents/{agent_id}/sessions/{session_id} | get_agent_session | Returns full session dict including DSL |
DELETE | /agents/{agent_id}/sessions/{session_id} | delete_agent_session_item | Single-session delete |
DELETE | /agents/{agent_id}/sessions | delete_agent_session | Bulk delete by ids or delete_all |
Session completion (running the workflow) flows through _run_workflow_session() , which handles both streaming (SSE via sse() inner generator) and non-streaming responses. The older completion() function in canvas_service.py is used by the embedded/iframe flow and the OpenAI-compatible endpoint.
The user_inputs SSE event signals a mid-workflow pause. Clients should re-POST to the completion endpoint with the same session_id and the collected inputs dict to resume execution.