How Project N.O.M.A.D. Manages AI Chat Sessions with Ollama: Architecture and Implementation

Project N.O.M.A.D. manages AI chat sessions with Ollama through a three-layer architecture comprising database models for persistence, a service layer for business logic, and HTTP controllers that handle streaming responses, RAG context injection, and automatic title generation.

Project N.O.M.A.D. (Network Operations Management and Development) is an open-source platform that orchestrates local LLM interactions through Ollama. The system implements a robust chat session management architecture that persists conversations, supports real-time streaming, and optionally enriches queries with retrieval-augmented generation (RAG) context. This article examines the complete lifecycle of AI chat sessions in Project N.O.M.A.D., from database schema to streaming implementation.

Three-Layer Architecture for Chat Management

Project N.O.M.A.D. organizes its chat functionality into distinct layers that separate data persistence, business logic, and HTTP handling.

Database Models

The persistence layer uses two core models defined in the admin/app/models/ directory:

These models establish a one-to-many relationship where each session contains multiple messages, enabling full conversation history retrieval.

Service Layer

The ChatService (admin/app/services/chat_service.ts) encapsulates all business logic for session management. Key methods include:

  • createSession (lines 31-38): Initializes a new chat session and returns its UUID.
  • addMessage: Persists user and assistant messages to the database.
  • generateTitle: Automatically creates session titles using the DEFAULT_QUERY_REWRITE_MODEL defined in constants/ollama.ts, falling back to message truncation if the model is unavailable.
  • getAllSessions (lines 14-24): Retrieves all sessions with eager-loaded messages.
  • deleteSession and deleteAllSessions (lines 76-88): Handle cleanup of individual or all sessions.

The service layer communicates with Ollama through OllamaService (admin/app/services/ollama_service.ts), which wraps the ollama NPM client and provides methods like chat, chatStream, and checkModelHasThinking.

Controller and Routes

The OllamaController (admin/app/controllers/ollama_controller.ts) serves as the HTTP entry point for chat operations. Routes are registered in admin/start/routes.ts under the /api/ollama prefix, including:

  • POST /api/chat/sessions: Creates new sessions via ChatsController.store.
  • GET /api/chat/sessions: Lists all sessions.
  • GET /api/chat/sessions/:id: Retrieves specific session history.
  • DELETE /api/chat/sessions/all: Admin endpoint for bulk deletion.
  • POST /api/ollama/chat: Primary chat endpoint handling both streaming and non-streaming requests.

Complete Session Lifecycle

Understanding how Project N.O.M.A.D. manages AI chat sessions requires examining the full lifecycle from creation to cleanup.

Creating a New Session

When a user initiates a conversation, the frontend sends a POST request to /api/chat/sessions. The ChatService.createSession method inserts a new row into the database and returns a UUID that subsequent requests use to maintain conversation context.

// POST /api/chat/sessions
await fetch('/api/chat/sessions', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ title: 'My New Chat' })
})
  .then(r => r.json())
  .then(data => console.log('Session ID:', data.id));

Implementation reference: ChatService.createSession in admin/app/services/chat_service.ts (lines 31-38).

Streaming AI Responses

For real-time interactions, Project N.O.M.A.D. implements Server-Sent Events (SSE). When the controller receives a request with stream: true, it sets appropriate SSE headers and calls OllamaService.chatStream. The controller writes each chunk in the format data: <json>\n\n directly to the client while accumulating the full response in memory.

const payload = {
  model: 'llama3',
  messages: [{ role: 'user', content: 'Explain quantum tunneling' }],
  stream: true,
  sessionId: 12   // optional, to persist the dialogue
};

const evtSource = new EventSource('/api/ollama/chat', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(payload)
});

evtSource.onmessage = ev => {
  const chunk = JSON.parse(ev.data);
  if (chunk.message?.content) {
    console.log('Chunk:', chunk.message.content);
  }
};

Implementation reference: OllamaController.chat streaming branch in admin/app/controllers/ollama_controller.ts (lines 24-35 and 29-35).

Persisting Messages and Context

The session management system maintains conversation continuity through automatic persistence:

  1. User message storage: When OllamaController.chat receives a request containing sessionId, it immediately stores the incoming user message via ChatService.addMessage.
  2. Assistant response accumulation: During streaming, the controller aggregates response chunks. After the stream completes, it persists the full assistant reply using ChatService.addMessage.
  3. Context window management: For subsequent turns, the controller retrieves message history from the database to provide the Ollama model with full conversation context.

Automatic Title Generation

Project N.O.M.A.D. automatically generates meaningful session titles when conversations begin. If a session contains two or fewer messages, ChatService.generateTitle executes a lightweight title-generation prompt using the default query rewrite model. If that model is unavailable, the system falls back to truncating the user's first message to create the title.

Advanced Chat Features

Beyond basic CRUD operations, Project N.O.M.A.D. implements sophisticated features for enhanced AI interactions.

Retrieval-Augmented Generation (RAG)

The chat controller optionally enriches prompts with relevant context from a document store. When query rewriting produces a non-empty string, the controller:

  1. Calls RagService.searchSimilarDocuments to fetch relevant documents.
  2. Calculates token budgets using getContextLimitsForModel based on the specific model's context window.
  3. Injects a system message containing the selected context before forwarding the request to Ollama.

This implementation allows the LLM to reference private knowledge bases while maintaining the conversational flow.

Model Thinking Capabilities

For models that support chain-of-thought reasoning, Project N.O.M.A.D. detects thinking capabilities through OllamaService.checkModelHasThinking. When enabled, the controller passes a think parameter (or 'medium' specifically for gpt-oss models) to activate internal reasoning traces, providing users with insight into the model's decision-making process.

Session Management API

Administrators and users can manage conversation history through dedicated endpoints:

Retrieving all sessions:

fetch('/api/chat/sessions')
  .then(r => r.json())
  .then(sessions => console.table(sessions));

Implementation reference: ChatService.getAllSessions in admin/app/services/chat_service.ts (lines 14-24).

Deleting all sessions:

await fetch('/api/chat/sessions/all', { method: 'DELETE' });

Implementation reference: ChatService.deleteAllSessions in admin/app/services/chat_service.ts (lines 76-88).

Summary

  • Three-layer architecture: Database models (ChatSession, ChatMessage), service layer (ChatService, OllamaService), and HTTP controllers (OllamaController) work together to manage AI chat sessions with Ollama.
  • Streaming implementation: Server-Sent Events provide real-time token streaming with proper SSE formatting (data: <json>\n\n) and automatic message persistence after stream completion.
  • RAG integration: The system optionally injects retrieved document context into system messages, respecting model-specific token limits via getContextLimitsForModel.
  • Automatic metadata: Sessions receive AI-generated titles using ChatService.generateTitle when conversations begin, falling back to message truncation if the title generation model is unavailable.
  • Full lifecycle support: From creation via createSession to cleanup via deleteAllSessions, the platform maintains conversation history with foreign-key relationships between sessions and messages.

Frequently Asked Questions

How does Project N.O.M.A.D. persist chat history?

Project N.O.M.A.D. persists chat history using Lucid ORM models defined in admin/app/models/chat_session.ts and admin/app/models/chat_message.ts. The ChatSession model stores session metadata, while ChatMessage stores individual messages with a foreign key relationship to their parent session. The ChatService.addMessage method handles the actual database insertion for both user and assistant messages.

What is the difference between streaming and non-streaming chat requests?

Streaming requests set stream: true in the payload, causing OllamaController.chat to invoke OllamaService.chatStream and return Server-Sent Events with headers appropriate for SSE. Non-streaming requests use OllamaService.chat, which waits for the complete response before returning. Both modes persist the final assistant message to the database, but only streaming provides real-time token delivery to the client.

How does the system handle context limits when using RAG?

When RAG is enabled, the controller calls getContextLimitsForModel to determine the maximum token budget based on the specific Ollama model being used. It then fetches relevant documents via RagService.searchSimilarDocuments and truncates or selects content to fit within the remaining context window after accounting for the conversation history and system prompts.

Can different chat sessions use different Ollama models?

Yes. The ChatSession model includes a model field that stores the specific Ollama model name for that session. When OllamaController.chat processes requests, it uses the model specified in the session record (or the request payload), allowing different conversations to run simultaneously with different models (e.g., llama3 for one session, mistral for another).

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →