Skip to main content

Request Flow

This diagram shows the end-to-end path of a single user message through the system — from the moment it hits the REST API to the final response.

Conversational Request Flow

API Layer

The request enters through ChatApiController, which exposes POST /api/chat/message (sync) and POST /api/chat-stream/sse (streaming). The MessageValidator sanitises the input (HTML stripping, length check, header validation) before ChatServiceImpl takes over as the top-level coordinator — loading or creating a session and handing the message to the orchestrator.

AI & Orchestration — Tool Calling

ChatOrchestrator is the central dispatcher. It builds a phase-gated set of tools via RootOrchestrationTools and passes them alongside the user message and a system prompt to the root LLM (via Spring AI ChatClient).

The root LLM selects which tool(s) to call based on the conversation phase and user message:

  • FormFillingTool — delegates to FormFillingToolHandler, which runs an inner LLM with FormTools (getFieldInfo, submitAnswer, clearAnswer, buildSummary, addExtraInfo, resetForm). The inner LLM reads the form schema and state, then drives form completion via tool calls
  • KnowledgeTool — delegates to KnowledgeQueryHandler for PgVector RAG + file-based knowledge retrieval
  • ConfirmationTool — delegates to ConfirmationHandler for booking confirmation, submission, and session archival (only available during PRE_CONFIRMATION phase)
  • GreetingTool / LanguageChangeTool / OutOfScopeTool — lightweight tools that perform direct session mutation; the root LLM composes the response naturally

After all tools execute, the root LLM synthesises a single conversational response that weaves all tool results together.

Session Layer

ConversationSessionManager manages session lifecycle. Active sessions are cached in Redis (30-minute TTL) for fast access. When a session expires from Redis, it can be restored from MongoDB (90+ day retention) for continuity or compliance.

Form Engine

When the inner LLM calls submitAnswer(fieldName, value), the value is validated by AnswerValidator (type coercion + validation rules) and stored in the session. The LLM reads the form structure via getFormSchema() and current progress via getFormState() to decide which field to ask about next. FormSchemaRenderer produces a flat, DFS-ordered schema with show_when conditions for conditional branches.