Multi-Task (DAG) Routing
Most assistants answer a complex request with a single AI call. Synaplan does something different: a small planner model turns the request into a DAG — a directed acyclic graph of tasks — and executes the steps in order, streaming a live task card for each one. Ask for several things at once and you get several real outputs back, on whatever channel the message arrived on.
This is the deep dive. For where it sits in the wider system (the Docker service map, SSE vs WebSocket streaming, the realtime layer) see Architecture & Realtime.
Why a plan instead of one call
A single prompt often hides several distinct jobs. "Summarise this PDF, translate the summary to German, and read it aloud" is three capabilities, not one. A flat chat completion has to fake that in one pass; a plan does each step with the right tool and lets you watch it happen.
| Single AI call | Multi-task DAG plan |
|---|---|
| One model, one answer | The right capability per step (RAG, search, media, document, calendar, …) |
| Files? Usually one, or none | Multiple generated files from one request |
| Opaque — you wait, then a wall of text | Transparent — a live card per step shows pending → running → done |
| Hard to extend | A typed capability registry you can grow |
Simple requests still take the classic fast path — one classifier decision, one handler, no planner overhead. The planner only engages for requests that the AI sorter sees as genuinely multi-step, and only when an administrator has enabled it.
A worked example
Prompt: "Can you create a short paragraph about DAG routing in AI models and create a reminder calendar entry for tomorrow at 10am?"
The planner emits a two-node plan. The chat UI shows a Task plan · 2/2 card while it runs, then leaves both results in place:
- Answer — a streamed text paragraph (
chatcapability). - Calendar invite — a downloadable
.icsmeeting file for tomorrow 10:00, with the date resolved to an absolute time (calendar_eventcapability).
┌──────────────────────────────┐
user request ──▶│ TaskPlanner (planner model │
│ → validated JSON DAG) │
└───────────────┬──────────────┘
▼
┌──────────────── DAG, executed in topological order ──────────────┐
│ n1: chat ───────────────┐ │
│ ▼ │
│ n2: calendar_event ──▶ compose_reply (terminal reply node) │
└───────────────────────────────────────────────────────────────────┘
│ live SSE cards: plan · task_update · task_chunk · task_file
▼
Answer text + meeting_YYYYMMDD_HHMMSS.ics
The relative date ("tomorrow at 10am") is resolved by injecting the current time
into the planner prompt, so the .ics lands on the right day in the right
timezone — no manual date math.
Capabilities
Each DAG node runs exactly one capability. The planner may only emit capabilities from this fixed, validated set — there is no arbitrary code execution.
| Capability | Does |
|---|---|
extract_text |
Read text from an uploaded attachment (Tika / OCR / Whisper) |
chat |
A normal text answer |
summarize |
Summarise input text |
translate |
Translate input text |
rag_query |
Semantic search over your knowledge base |
web_search |
Live web search with a generated query |
file_analysis |
Vision / OCR / document question-answering |
image_generation |
Generate an image (the /pic path) |
video_generation |
Generate a video (the /vid path) |
text2sound |
Text-to-speech audio |
document_generation |
Build a CSV / XLSX / DOCX / PPTX file |
calendar_event |
Build an .ics calendar invite |
email_me |
Email the result to the account owner |
A hidden compose_reply node always terminates the graph — it assembles the final
text plus any attachments into the single reply you see.
Steps pass data along the edges with a small reference grammar
($message.text, $n1.text, $n1.file, …), so one node can consume what an
earlier node produced.
How a plan executes
- Plan — the planner model returns JSON; Synaplan validates it (known capabilities only, valid dependencies, no cycles, a sane node cap, a valid reply node). Invalid output safely falls back to a single chat answer.
- Execute — nodes run in topological (dependency-first) order. With parallel mode enabled, independent media nodes (image/video/audio) are offloaded to concurrent subprocesses while text nodes stream inline.
- Isolate failures — if a node fails, only the steps that depended on it are skipped; the rest of the plan still delivers. If everything fails, the turn falls back to the classic single-handler path.
- Assemble & deliver — results (including multiple files) are delivered on the originating channel: chat, widget, WhatsApp, email, or webhook. Web chat additionally persists the card states, so the task plan is still there after a reload.
Live progress events (SSE)
Task plans stream over the same Server-Sent Events channel as normal answers
(/api/v1/messages/stream). Alongside the answer tokens you get plan events:
Event (status) |
Fires when | Key fields |
|---|---|---|
plan |
A multi-node plan starts | the node list (node_id, capability, kind), reply_node |
task_update |
A node changes state | node_id, state (pending → running → done / failed / skipped) |
task_chunk |
A text node streams a token | node_id, chunk |
task_file |
A node produced a file | node_id, type, url |
task_progress |
A long media render advances | node_id, percent, provider status |
See Code Examples for the SSE client pattern.
Enabling & configuring
Multi-task routing is controlled per user under Settings → Routing in the app. Existing installs keep the classic single-handler fast path until it is switched on, and a shadow mode can plan without executing so operators can review what the planner would do before turning it loose.
Behind the scenes these map to the MULTITASK configuration group
(ROUTING_ENABLED, SHADOW_MODE, PARALLEL_ENABLED, MAX_PARALLEL,
NODE_TIMEOUT) plus the classifier's fast-path flag. Developer notes live in
docs/DEVELOPMENT.md
in the main repository.
Extending the graph
Capabilities are a typed registry, not a hardcoded prompt: a Capability
value plus a tagged task-runner service. Today's set are thin adapters over
capabilities Synaplan already runs in production, which keeps the graph safe and
predictable.
On the roadmap: open DAG endpoints. We're working toward DAG nodes that hand off to n8n and other open-source services — so the planner can orchestrate the self-hosted stack you already operate. The AI does the planning; your tools do the work, on your infrastructure. This is a particularly good fit for self-hosting teams who want an AI front door to their existing automations rather than a closed black box.
See also
- Architecture & Realtime — where routing sits in the stack
- Code Examples — SSE streaming client
- MCP Server — expose your knowledge and memories as tools
- GitHub: synaplan — source for the planner and DAG executor