Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/FloxTBoTyy/BoardPulse-AI/llms.txt

Use this file to discover all available pages before exploring further.

BoardPulse AI is built around a strict separation between the interface layer, the orchestration core, and the data layer. Every natural-language question travels through a deterministic five-node LangGraph graph before a single byte of SQL reaches your database. This page explains each layer and how they connect.

Services

The full deployment runs six Docker services defined in docker-compose.yml:
ServiceImage / FrameworkDefault portRole
apiFastAPI (Python)8000Core backend — orchestration, SQL execution, exports
adminNext.js3001Administrative panel for workspace and schema configuration
postgresPostgreSQL 165432Internal config store, audit log, and demo data
redisRedis 76379Cache and job queues
openwebuiOpen WebUI3002Conversational chat interface
ollamaOllama11435Local LLM inference (optional, local-models profile)
Ollama is only started when you use docker compose --profile local-models up. All other services start without it.

Conversation graph

Every query sent to POST /api/v1/chat/query is processed by a LangGraph StateGraph defined in backend/app/graphs/chat_graph.py. The graph runs five nodes in a fixed linear sequence with no branching:
load_context → draft_query → validate_query → execute_query → finalize_response
Each node receives and updates a shared ChatState object. Here is what each node does:

load_context

Calls metadata_service.get_workspace_context() to retrieve the workspace’s approved table list, schema metadata, and dialect information. This context is passed to every subsequent node — the AI never sees tables that are not on the approved list.

draft_query

Calls model_router.draft_query() with the user’s message and the loaded context. The model router selects a backend (cloud, local, or mock) based on the preferred_provider field in the request, then uses the chosen LLM to produce a candidate SQL query and a plain-language answer draft.

validate_query

Passes the candidate SQL through sql_guard.validate_read_only_query(). This is the SQLGlot-based guardrail layer — see SQL guardrail layer below.

execute_query

Runs the validated SQL against your source database in strict read-only mode via query_service.execute_read_only_query(). Also builds CSV/XLSX exports, assembles a chart specification if the data shape supports it, and composes the final answer text.

finalize_response

Appends any warnings collected during the run (for example, if row results were capped by QUERY_MAX_ROWS) to the answer text, then returns the complete ChatResponse.
The executed_sql field in every API response contains the exact SQL that ran — nothing is hidden. You can use this for auditing or to reproduce queries directly against your database.

Model router

The model router (backend/app/services/model_router.py) decouples the graph from any specific LLM backend. Three providers are supported:
ProviderConfigurationUse case
mockNo API key requiredDevelopment, testing, CI
cloudSet OPENAI_API_KEY and OPENAI_MODEL in .envProduction with OpenAI or compatible API
localSet OLLAMA_MODEL and enable the local-models profileAir-gapped or privacy-sensitive deployments
The provider is selected per request via the preferred_provider field in ChatRequest, or falls back to DEFAULT_MODEL_PROVIDER from your environment. The graph logic is identical regardless of which provider runs — only the LLM call changes.

SQL guardrail layer

Before any SQL reaches your database, it passes through a validation layer built on SQLGlot. The guard (backend/app/services/sql_guard.py) enforces:
  • SELECT onlyINSERT, UPDATE, DELETE, DROP, ALTER, and any other mutation statements are rejected at parse time
  • Table allowlist — queries referencing tables not in DEFAULT_SOURCE_INCLUDE_TABLES are blocked, even if those tables exist in the database
  • Row limit — results are capped at QUERY_MAX_ROWS (default 200) to prevent runaway queries
The guardrail operates at the SQL AST level using SQLGlot’s parser, not with string matching. Obfuscated or nested attempts to bypass the SELECT restriction are caught.

Data flow diagram

User / Open WebUI


POST /api/v1/chat/query (FastAPI)


  LangGraph ChatGraph
  ┌─────────────────────────────────────────┐
  │  load_context                           │
  │       │                                 │
  │       ▼                                 │
  │  draft_query  ←── model_router          │
  │       │         (cloud / local / mock)  │
  │       ▼                                 │
  │  validate_query  ←── sql_guard          │
  │       │              (SQLGlot)          │
  │       ▼                                 │
  │  execute_query  ←── source database     │
  │       │              (read-only)        │
  │       ▼                                 │
  │  finalize_response                      │
  └─────────────────────────────────────────┘


  ChatResponse (answer, sql, table, chart, exports)

OpenAI-compatible endpoint

Open WebUI connects to BoardPulse AI through an OpenAI-compatible endpoint at /openai/v1. This allows Open WebUI to treat BoardPulse AI as if it were an OpenAI model, with no changes to Open WebUI’s configuration beyond pointing it at http://api:8000/openai/v1. The BoardPulse AI backend handles the translation — the full LangGraph conversation graph runs on every message regardless of which interface sent it.

SQL guardrails

Full details on what the validation layer blocks and why

Model providers

How to configure cloud, local, and mock provider settings

Connect a database

Point the system at your own PostgreSQL instance

Docker deployment

Service-by-service breakdown of the Docker Compose setup

Build docs developers (and LLMs) love