Chat pipeline
ChatController owns the active conversation and the full turn pipeline. It coordinates persistence, memory, device context, routing, search, model streaming, tool continuation, haptics, sources, and title generation.
Controller state
The controller builds ChatState from saved preferences:
webSearchfromStorageKeys.webSearchEnabled.thinkingfromStorageKeys.thinkingEnabled.agentToolsfromStorageKeys.agentToolsEnabled.
It also starts loading the assistant model with loadAssistant() so the first turn is faster.
Opening and creating chats
Opening a chat cancels any active generation, stops writing haptics, loads messages from ChatRepository, and updates state with the selected conversation id.
Starting a new chat clears the active subscription and keeps the current default toggles.
When the first message is sent:
- A new conversation id is generated by
ChatRepository. - The conversation row is created with a local deterministic title.
- The user message is inserted into SQLite.
- An empty assistant placeholder is added to state.
- The drawer provider is invalidated so the conversation list updates.
Send flow
The send method follows this sequence:
- Trim input and reject empty turns without an image.
- Reject images when the active model does not support vision.
- Save the user message and optional image.
- Add an assistant placeholder.
- Store the user message in vector memory asynchronously.
- Route the text through
ToolRouter. - Complete instantly for deterministic device commands or device info questions.
- Prepare exact math context when applicable.
- Rewrite likely follow-up fragments into standalone requests.
- Recall profile facts and vector memories.
- Read device context in parallel with memory recall.
- Search the web if the toggle or autonomous router requires it.
- Build the system prompt through
SystemIdentity.build. - Optionally perform deep reasoning.
- Stream the model response into the assistant placeholder.
Routing
ToolRouter combines deterministic parsing and utility model checks.
It handles:
- Exact math through
MathEval. - Timers, alarms, clock actions, app opening, media control, and ringer mode.
- Device info questions about time, date, battery, storage, memory, apps, and recent apps.
- Web search decisions.
- Query planning and reflection for multi-hop search.
- Standalone rewrite for follow-up messages.
- App package selection from the real installed app catalog.
Obvious non-search shapes stay local. Ambiguous search decisions default to no search unless the utility model returns a strict YES.
Prompt construction
The system prompt is built by SystemIdentity. It includes:
- Munin identity and behavioral rules.
- Current date and knowledge cutoff.
- Web availability or web search grounding rule.
- Coarse location from timezone.
- Device context if available.
- Profile facts and vector memory snippets.
- Thinking mode rule.
- Tool fence instructions.
- Agent context when agent tools are enabled.
- Card fence instructions for compose-style outputs.
The prompt is terse because the target model is small. Dynamic blocks are injected only when relevant.
Streaming and UI updates
Model output arrives as GenerationChunk values. The controller buffers answer and thinking chunks, then flushes on a timer. This avoids rebuilding the message list for every token.
When streaming starts:
PipelineStagemoves to thinking or writing.- A soft haptic tick runs periodically during writing.
- Thinking chunks go into the message thinking field.
- Answer chunks go into the message text field.
When streaming finishes:
- The assistant placeholder is marked not streaming.
- Sources are attached.
- The message is saved to SQLite.
- The assistant message is stored in vector memory.
- A generated title may replace the initial local title.
- Profile facts are learned asynchronously.
- The conversation list is refreshed.
Tool continuation
The assistant can request a mid-answer tool call with a munin-tool fenced JSON block. The controller removes the fence from the visible message, executes the tool, and resumes generation with the tool result.
Supported tool calls include:
searchmathloadSkillrunSkillrunIntentrunMcp
The ReAct loop is capped at two hops for one turn. This prevents runaway tool loops on-device.
Instant completions
Device commands and device info questions can complete without a model round trip. The controller creates branded output cards and settles the assistant placeholder immediately.
Examples:
- Set a timer.
- Set an alarm.
- Report battery or storage.
- Open a clock or app.
- Control media playback.
Regeneration
Regeneration removes the last assistant response and resends the preceding user message. The controller reuses the same send pipeline, so search, memory, routing, and tool behavior are recalculated.