Skip to content

AI-Assistant Module

Location: apps/api/src/modules/ai-assistant/

Aggregate roots: AssistantSession, AssistantTurn, AssistantSuggestion

The AI-assistant context implements the 5-question reflection flow that discovers a user's interests. It uses either a scripted prompt system (default, no API key needed) or Anthropic Claude Sonnet via @ai-sdk/anthropic when ANTHROPIC_API_KEY is set. After the session completes, the mapInterests endpoint produces AssistantSuggestion records the user can apply to their profile.


Prisma Models

ModelTableNotes
AssistantSessionassistant_sessionsOne session per reflection run
AssistantTurnassistant_turns5 turns per session (step 1..5)
AssistantSuggestionassistant_suggestionsInterest suggestions derived from session

AssistantSession Fields

FieldTypeNotes
idUUID PK
userIdUUID FK
statusStringin_progress | completed | abandoned
topicString?Starting topic (interest slug or free-form)
summaryString?AI-generated summary
startedAtDateTime
completedAtDateTime?

AssistantTurn Fields

FieldTypeNotes
stepInt1..5
promptStringAssistant question
answerString?User reply (null until answered)
answeredAtDateTime?

Unique: (sessionId, step)

AssistantSuggestion Fields

FieldTypeNotes
interestIdUUID FKSuggested interest
confidenceFloat0..1 from model or keyword score
depthInt (1..5)Suggested engagement depth
reasonString? (280)Model reasoning
appliedBooleanTrue once user accepted → UserInterest upserted

Key Methods

MethodDescription
start(userId, topic)Create session, seed first prompt, emit ai.session.started
findSession(userId, sessionId)Fetch session with all turns
listMine(userId, limit)List user's sessions newest-first
answer(sessionId, userId, answer)Submit answer, generate next prompt, advance step
complete(sessionId, userId)Mark session completed, emit ai.session.completed
mapInterests(sessionId, userId)Analyse turns → create AssistantSuggestion rows
applySuggestions(sessionId, userId, ids)Upsert UserInterest for accepted suggestions

HTTP Endpoints

All under /api/v1/ai/. Require JwtAuthGuard.

MethodPathDescription
POST/ai/sessionsStart a new session { topic? }
GET/ai/sessionsList my sessions
GET/ai/sessions/:idGet session with turns
POST/ai/sessions/:id/answerSubmit answer to current turn { answer }
POST/ai/sessions/:id/completeMark session completed
POST/ai/sessions/:id/map-interestsRun interest mapping → returns suggestions
POST/ai/sessions/:id/apply-suggestionsApply selected suggestions { ids: string[] }
POST/ai/enhance-postAI content enhancement { title, body, interestSlug? }

Events Emitted

EventPayloadConsumed by
ai.session.started{ sessionId, userId, topic }(observability)
ai.turn.answered{ sessionId, userId, step }(observability)
ai.session.completed{ userId, sessionId }Notifications

Two Backends

ModeConditionBehaviour
ScriptedNo ANTHROPIC_API_KEYPredefined question templates from scripted-questions.ts, keyword-based interest matching
AnthropicANTHROPIC_API_KEY presentClaude Sonnet 4.6 via Vercel AI SDK, streaming responses, semantic interest inference

The scripted fallback uses renderPrompt(step, topic, priorAnswers) to produce contextually aware questions without any LLM cost.

Interest Mapping (mapInterests)

  1. Concatenate all 5 turn answers into a single transcript.
  2. Either: send to Claude with the interest catalog as context (Anthropic mode), or run keyword overlap scoring (scripted mode).
  3. Create AssistantSuggestion rows with confidence, depth, and reason.
  4. Return suggestions grouped by confidence tier.

Apply Suggestions

  1. User selects suggestion IDs to accept.
  2. For each accepted ID: upsert UserInterest { depth, expertise }.
  3. Mark suggestion applied = true.
  4. Emit interests.user_selected so Reputation seeds +5 per top-level interest.

Regulus — invite-only social-knowledge platform