Salud IA Bot is structured as a layered NestJS application where every incoming Telegram message travels through a well-defined pipeline: from the bot controller at the top, through a set of specialized domain services in the middle, down to a SQLite database and external APIs at the base. This separation ensures that each layer has a single responsibility and that new regions or health domains can be added without touching unrelated code.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/RubenDarioGuerreroNeira/Ecosistema-IA-Colombia/llms.txt
Use this file to discover all available pages before exploring further.
Layered Architecture
Bot Layer
BotUpdate — the single NestJS-Telegraf @Update() controller. Receives all Telegram events (@Start, @Help, @On('text'), @On('location')) and dispatches them to the correct domain service.Services Layer
Specialized domain services for public health, mental health, sexual health, regional providers, air quality, predictive analytics, and chart generation. Each service owns its own query logic.
Data Layer
TypeORM repositories backed by
better-sqlite3. All persistent data lives in data/salud-ia-bot.db. External calls are limited to OpenRouter (AI) and QuickChart (graphs).Module Structure
The application is bootstrapped through three NestJS modules that compose the full feature set.AppModule
The root module. It wires together the configuration validator, the Telegram client, the database connection, and the bot feature module.ConfigModule is set to isGlobal: true, making the validated environment values available in every provider without re-importing the module. Joi ensures the application fails fast on startup if TELEGRAM_BOT_TOKEN or OPENROUTER_API_KEY are missing.
BotModule
Registers theBotUpdate controller plus all companion services that BotUpdate depends on directly. It also imports StatsModule, DataModule, and DatabaseModule to give BotUpdate access to TypeORM repositories and pre-built query services.
DataModule
Handles TypeORM entity registration. All eight entities —BoyacaProvider, AntioquiaProvider, CaliProvider, YopalProvider, Vaccination, MentalHealth, SexualHealth, and HealthEvent — are declared here via TypeOrmModule.forFeature([...]). Every domain service that needs a repository is also provided and exported from this module so BotModule can consume them without re-declaring the entities.
BotUpdate Event Handlers
BotUpdate is decorated with @Update() from nestjs-telegraf and exposes four entry points for all user interactions:
@Start()
Fires when a user sends
/start. Calls sendPersonalizedGreeting(), which renders a time-aware greeting (“Buenos días / Buenas tardes / Buenas noches”), addresses the user by their Telegram first name, and displays the full capabilities menu. UserService.markAsGreeted() records the interaction so subsequent sessions receive a shorter greeting.@Help()
Fires on
/help. Assembles eight separate Markdown sections (public health, mental health, sexual health, provider search, predictions, charts, air quality, vaccination) and sends each through sendLongMessage() to respect Telegram’s message-length limit.@On('text')
The main dispatcher for all free-text user messages. Runs up to 15 prioritized handler checks in sequence — the first handler that returns
true short-circuits the chain. See the intent routing section below for the full priority order.@On('location')
Fires when the user shares a GPS location via Telegram. Reads
latitude and longitude from the Telegram update, clears any pending provider_search_location intent from userState, then unconditionally calls YopalHealthService.findNearby(lat, lon, radiusKm) with a default radius of 5 km. Returns up to five nearby providers formatted with address, phone number, and distance in km.Intent Routing in @On('text')
Every free-text message enters a sequential priority chain. The first handler that claims the message stops the chain; unmatched messages fall through to the GenkitService AI fallback.
| Priority | Trigger condition | Handler / Service |
|---|---|---|
| 0 | ”Que puedes graficar?”, “Que riesgos puedes predecir?” | handleServiceCapabilitiesQuery — returns capability listings without querying the LLM |
| 1 | Structural counts / list queries | handleStructuralDataQuery -> SaludPublicaQuestionsService |
| 2 | Mental health diagnoses, CIE-10 profiles | MentalHealthQuestionsService.handleMentalHealthQuery |
| 2.5 | SIVIGILA rankings, gender/age breakdowns | handleSaludPublicaQuestions -> SaludPublicaQuestionsService |
| 3.5 | alertas tempranas, pronostico, prediccion, proyeccion+casos, clasificar riesgo | handleNewPredictiveServices -> PredictiveQuestionsService |
| 4 | GRAPHIC_KEYWORDS (graficar, grafico, chart) | handleChartQuery -> ChartQueryService / GraphicsQuestionsService |
| 5 | GREETING_REGEX match | handleGreeting — sends full menu to new users; sends shortened greeting to returning users who say a greeting keyword |
| 6 | Mentions “cali” | handleServiceCali -> CaliHealthService (urgency detection, zero-LLM bypass) |
| 6.5 | Mentions “antioquia” | handleAntioquiaQuery -> AntioquiaQuestionsService |
| 7 | Provider search keywords | handleProviderSearch -> SaludPublicaQuestionsService |
| 8 | YOPAL_KEYWORDS | handleYopalQuery -> YopalQuestionsService |
| 9 | predecir riesgo / predecir casos | handlePrediction -> PredictionService |
| 10 | calidad del aire, aire, ENVIRONMENTAL_KEYWORDS | handleAirQualityQuery -> AirQualityService |
| 11 | General stats context | StatsService.getSummary — if response contains BYPASS_MARKERS, reply directly |
| 12 | Sexual health keywords | handleSexualHealthQuery -> SexualHealthService |
| 13 | clasificar riesgo, analizar riesgo de, machine learning, inteligencia artificial | handleMLClassification -> PredictiveQuestionsService.clasificarRiesgo |
| 14 | Named public health event | handleSaludPublica -> SaludPublicaService.procesarPregunta |
| 15 | Everything else | handleGeneralQuery -> GenkitService.generateResponse (RAG-augmented) |
Message Length Management
Telegram enforces a maximum message length of 4,096 characters. Salud IA Bot uses a conservative internal constant ofMAX_MESSAGE_LENGTH = 4000 to leave a small buffer. All responses are passed through sendLongMessage() before being sent:
\n boundaries preserves Markdown list formatting across message fragments and prevents mid-word cuts in formatted output.
User Session Management
BotUpdate maintains an in-memory Map<number, UserState> keyed by Telegram user ID. A UserState holds an intent string and an optional data payload:
- Location request: user asks for nearby providers -> bot sets intent
provider_search_location-> user shares GPS ->@On('location')handler clears the pending intent and callsYopalHealthService.findNearby(). - Region disambiguation: user asks about a disease without specifying a region -> bot prompts for a department -> next short text message is caught by
handleConversationContinuity()and resumes the original intent.
UserService (injected from the database layer) provides a persistent greeted-state tracker so the bot sends a full welcome only on the first interaction, then a shorter greeting on repeat visits.
Service Inventory
| Service | Purpose |
|---|---|
GenkitService | OpenRouter / LLaMA 3.1 AI client with exponential-backoff retry |
UserService | Tracks per-user greeted state across sessions |
StatsService | Aggregates summary context from multiple data sources |
SaludPublicaService | SIVIGILA NLP engine — rankings, comparatives, urban/rural, age/gender breakdowns |
SaludPublicaQuestionsService | Intent classifier for structured public health queries |
SaludAnaliticaService | RAG orchestrator — merges SIVIGILA, vaccination, and air quality into prompts |
HealthDataService | Low-level TypeORM queries on the HealthEvent table |
HealthStatsService | Statistical helpers (ranking, time series projection) |
MentalHealthService | CIE-10 diagnosis search and profile generation |
MentalHealthQuestionsService | Intent router for mental health queries |
SexualHealthService | Keyword search over the sexual health Q&A dataset |
AirQualityService | Reads air quality indicators from SQLite by municipality |
AirQualityQuestionsService | Formats air quality capability listings and responses |
VaccinationService | PAI vaccination coverage queries by department |
YopalHealthService | Provider search with Haversine distance calculation |
YopalQuestionsService | Intent classifier for Yopal-specific queries |
CaliHealthService | Zero-LLM urgency and service detection for Cali providers |
AntioquiaHealthService | Provider search for Antioquia municipalities |
AntioquiaQuestionsService | Intent classifier for Antioquia-specific queries |
BoyacaHealthService | Provider search for Boyaca municipalities |
ChartService | Builds QuickChart URLs for bar, pie, and line charts |
ChartQueryService | Detects chart intent and dispatches to ChartService |
GraphicsQuestionsService | Returns the catalog of available chart types |
PredictiveQuestionsService | Orchestrates ML scoring, time-series forecasting, and early warnings |
MlPredictionService | Composite multidimensional risk scoring (SIVIGILA + vaccination + rurality) |
AdvancedPredictionService | Holt-Winters-inspired time series decomposition and projection |
EarlyWarningService | Threshold-based automated outbreak alert generation |
DatasetBuilderService | Normalizes and caches data tensors for ML models |
PredictionService | General risk prediction entry point |
NationalHealthService | National-level aggregated health statistics |
IngestaDatosService | Utility service for data ingestion workflows |