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.
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 withFormTools(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
KnowledgeQueryHandlerfor PgVector RAG + file-based knowledge retrieval - ConfirmationTool — delegates to
ConfirmationHandlerfor 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.