feat(web,api): Async initial question generation with generating state UI#66
Merged
feat(web,api): Async initial question generation with generating state UI#66
Conversation
- Add mocks for getDefaultModel and thought node updateOne, set node depth/fromSuggestion in fixtures, and update assertions to ensure no synchronous assistant message is created. - Verify enqueueMapNodeRequest is called with defaultModel and isInitialQuestion, and that the async response includes jobId and position.
- Enqueue server-side AI jobs to generate opening Socratic questions for empty node dialogues. - Add buildThoughtMapInitialQuestionPrompt, processInitialQuestion handler and resolveSiblingLabels helper. - Update controller to return 202 Accepted and mark the legacy generateThoughtMapInitialQuestion as deprecated.
Support async start responses when the AI initial question is generated via the queue. Key changes: - API/types: start response can be async (jobId, position) or sync (initialQuestion) - Seed cache for async case with an empty messages page so SSE can stream the AI message when ready - Optimistically mark node isExplored in the thought-map store on start to reflect backend persistence immediately - Invalidate thought map detail on stream completion so worker-set node changes are reflected in the UI
Use dialogue presence (hasDialogue) to control the Start button and display a 'Generating initial question…' loader while the AI produces the first message
There was a problem hiding this comment.
Pull request overview
This PR updates Thought Map node dialogue startup to support an async “initial question generation” flow (queue + SSE), and surfaces a “generating” UI state in the web dialogue panel while the AI produces the opening message.
Changes:
- Introduces a sync/async union response for “start dialogue” (existing message returned immediately vs.
{jobId, position}when queued). - Updates web hooks/components to seed caches appropriately and show a “Generating initial question…” state after dialogue creation.
- Adds a dedicated queue worker path + prompt builder for AI-generated initial questions.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/mukti-web/src/types/thought-map.ts | Splits start-dialogue response into async/sync interfaces and a union type. |
| packages/mukti-web/src/lib/hooks/use-thought-map-dialogue.ts | Seeds React Query cache for async start; optimistically marks nodes explored; invalidates map detail on SSE complete. |
| packages/mukti-web/src/lib/api/thought-map-dialogue.ts | Expands backend response parsing to handle optional jobId/position vs initialQuestion. |
| packages/mukti-web/src/components/thought-map/ThoughtMapDialoguePanel.tsx | Shows generating state when dialogue exists but no messages have arrived yet. |
| packages/mukti-api/src/modules/thought-map/thought-map-dialogue.controller.ts | Switches start dialogue to enqueue initial question generation and return 202 + job metadata. |
| packages/mukti-api/src/modules/thought-map/services/thought-map-dialogue-queue.service.ts | Adds isInitialQuestion job routing and an initial-question processing pipeline (prompt + SSE message emission). |
| packages/mukti-api/src/modules/thought-map/tests/thought-map-dialogue.controller.spec.ts | Updates controller tests for the new async start-dialogue behavior. |
| packages/mukti-api/src/modules/dialogue/utils/prompt-builder.ts | Adds buildThoughtMapInitialQuestionPrompt and deprecates the old static initial-question generator. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
packages/mukti-api/src/modules/thought-map/thought-map-dialogue.controller.ts
Show resolved
Hide resolved
packages/mukti-api/src/modules/thought-map/services/thought-map-dialogue-queue.service.ts
Show resolved
Hide resolved
- Group ghost suggestions by parent and compute centred Y positions using a new GHOST_VERTICAL_SPACING. - Prefer existing persisted/dragged positions when available to avoid jumps. - Persist computed/dragged ghost positions in a ghostPositionsRef and update drag-stop handling to save ghost positions to the ref instead of calling updateNode.
- Introduce GHOST_HORIZONTAL_OFFSET = 360 and update the thought-map layout to clear the rendered width of wide TopicNode (max-w-[320px]). - The 360px offset provides roughly 40px horizontal clearance given React Flow's top-left anchoring.
- Add tests for BYOK and server key model resolution - Also cover errors when server key or user record is missing
- Fetch the user's encrypted OpenRouter key and preferences, decrypt and resolve the effective model via aiPolicyService. - Fall back to the server OPENROUTER_API_KEY only if BYOK is absent. Throw 404 when user not found and 500 when no API key is available.
- Show "Generating initial question" loader when a dialogue exists but no messages yet. - Only render the processing "Mukti is thinking..." loader when there are existing messages and processing is active.
- Add buildThoughtMapInitialQuestionPrompt and remove duplicate instance. - Relocate and document centredYPositions helper for thought-map layout. - Tidy auth imports, fix DTO decorator order, and cleanup web components (import order, strict equality check, braces)
- Add optional position parameter to acceptGhostNode and pass the ghost's canvas coordinates when accepting AI-suggested nodes. - The backend create endpoint ignores position, so the client strips x/y before POSTing, overrides the returned node's position in the store, and then fire-and-forget updates the node to persist the intended position. - Failures to persist are logged but do not block the UI.
- Split QuestionNode into GhostNode and AcceptedQuestionNode. - Add framer-motion entrance and "ink solidifies" acceptance animations; introduce ENTRANCE_ANIMATION_WINDOW_MS and respect prefers-reduced-motion. - Delay onAccept (500ms) to allow animations to complete.
Implement comprehensive unit tests for `processInitialQuestion` within the `ThoughtMapDialogueQueueService`. These tests verify: - Correct SSE event sequences (processing, progress, message, complete) - Proper AI prompt construction with node context and sibling labels - Scoped data persistence via `dialogueService.addMessage` - Usage event tracking for initial question generation - Error handling and SSE error event emission - Correct technique selection logic based on node depth/type
Update the `ApiStartThoughtMapNodeDialogue` decorator to accurately reflect the async (202 Accepted) and sync response paths for initial Socratic question generation.
- Add validation for jobId and initialQuestion in thoughtMapDialogueApi - Refactor useStartThoughtMapDialogue to use functional state updates - Ensure ghost nodes are only removed on successful node creation - Improve error rollback logic in thought-map-store delete method
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
{ jobId, position }(202 Accepted) for new dialogues, or{ initialQuestion }for existing ones.ThoughtMapDialoguePanelshows a "Generating initial question..." loader while the AI produces the opening Socratic question via SSE.prefers-reduced-motion.setState, conditional ghost removal on success only.Changes
API (
@mukti/api)thought-map-dialogue.controller.ts— Start endpoint enqueues AI job for new dialogues, returns union response (async/sync)thought-map-dialogue-queue.service.ts— AddedprocessInitialQuestionworker path with dedicated prompt, SSE emissions, and usage event loggingprompt-builder.ts— AddedbuildThoughtMapInitialQuestionPromptwith sibling label contextdialogue-ai.service.ts— Handle empty user message for initial question generationthought-map-dialogue.swagger.ts— Updated Swagger to document union response with 202 status for both pathsconvert-canvas.dto.ts— Madetitleoptional inConvertCanvasDtoauth.controller.ts— Minor lint fixesWeb (
@mukti/web)ThoughtMapDialoguePanel.tsx— Added generating state detection (hasDialogue && !hasHistory)use-thought-map-dialogue.ts— Seed React Query cache for async start, functionalsetStateforisExplored, invalidate map detail on SSE completethought-map-dialogue.ts— Replaced!assertions with explicit runtime guards for backend response validationthought-map-store.ts— Conditional ghost removal onaddNodesuccess, functionalsetStatefordeleteNoderollbackThoughtMapCanvas.tsx— Ghost node layout: wider offsets, centered Y positions, persisted positions refQuestionNode.tsx— Reworked animations: acceptance ink stroke, entrance spring, countdown sweep with reduced-motion supportthought-map-layout.ts— Updated layout spacing for ghost node offsetsthought-map.ts— SplitStartDialogueResponseinto async/sync union typeTests
thought-map-dialogue.controller.spec.ts— Updated for async start behaviour, BYOK paths, error casesthought-map-dialogue-queue.service.spec.ts— New: 9 tests forprocessInitialQuestion(SSE events, prompt building, message persistence, usage logging, error path)thought-map-canvas.test.ts— Updated for new layout spacingthought-map.dto.spec.ts— Accept omitted title inConvertCanvasDtoTesting
bun nx run @mukti/api:test --testPathPattern="thought-map"— 11 suites, 160 tests passbun nx run @mukti/api:lint— 0 errorsbun nx run @mukti/web:lint— 0 errorsScreenshots (if applicable)
Checklist
Additional Notes
Addresses all 4 Copilot review comments:
!assertions in API clientsetStateto prevent Zustand race conditionsprocessInitialQuestionworker path