Skip to main content

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.

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.

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.
@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      validationSchema: Joi.object({
        TELEGRAM_BOT_TOKEN: Joi.string().required(),
        OPENROUTER_API_KEY: Joi.string().required(),
        OPENROUTER_MODEL: Joi.string().default(
          'nvidia/nemotron-3-super-120b-a12b:free',
        ),
        OPENROUTER_BASE_URL: Joi.string().default(
          'https://openrouter.ai/api/v1',
        ),
        PORT: Joi.number().default(3000),
        TELEGRAM_PROXY_URL: Joi.string().optional().allow(''),
        TELEGRAM_API_TIMEOUT: Joi.number().default(30000),
      }),
    }),
    TelegrafModule.forRootAsync({ ... }), // token + optional proxy
    DatabaseModule,
    BotModule,
  ],
})
export class AppModule {}
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 the BotUpdate 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.
@Module({
  imports: [StatsModule, DataModule, DatabaseModule, CacheModule.register(), ConfigModule.forRoot()],
  providers: [
    BotUpdate,
    GenkitService,
    UserService,
    IngestaDatosService,
    PredictiveQuestionsService,
    AirQualityQuestionsService,
    SaludAnaliticaService,
    AntioquiaHealthService,
    AntioquiaQuestionsService,
  ],
  exports: [IngestaDatosService, SaludAnaliticaService, DataModule],
})
export class BotModule {}

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:
1

@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.
2

@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.
3

@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.
4

@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.
PriorityTrigger conditionHandler / Service
0”Que puedes graficar?”, “Que riesgos puedes predecir?”handleServiceCapabilitiesQuery — returns capability listings without querying the LLM
1Structural counts / list querieshandleStructuralDataQuery -> SaludPublicaQuestionsService
2Mental health diagnoses, CIE-10 profilesMentalHealthQuestionsService.handleMentalHealthQuery
2.5SIVIGILA rankings, gender/age breakdownshandleSaludPublicaQuestions -> SaludPublicaQuestionsService
3.5alertas tempranas, pronostico, prediccion, proyeccion+casos, clasificar riesgohandleNewPredictiveServices -> PredictiveQuestionsService
4GRAPHIC_KEYWORDS (graficar, grafico, chart)handleChartQuery -> ChartQueryService / GraphicsQuestionsService
5GREETING_REGEX matchhandleGreeting — sends full menu to new users; sends shortened greeting to returning users who say a greeting keyword
6Mentions “cali”handleServiceCali -> CaliHealthService (urgency detection, zero-LLM bypass)
6.5Mentions “antioquia”handleAntioquiaQuery -> AntioquiaQuestionsService
7Provider search keywordshandleProviderSearch -> SaludPublicaQuestionsService
8YOPAL_KEYWORDShandleYopalQuery -> YopalQuestionsService
9predecir riesgo / predecir casoshandlePrediction -> PredictionService
10calidad del aire, aire, ENVIRONMENTAL_KEYWORDShandleAirQualityQuery -> AirQualityService
11General stats contextStatsService.getSummary — if response contains BYPASS_MARKERS, reply directly
12Sexual health keywordshandleSexualHealthQuery -> SexualHealthService
13clasificar riesgo, analizar riesgo de, machine learning, inteligencia artificialhandleMLClassification -> PredictiveQuestionsService.clasificarRiesgo
14Named public health eventhandleSaludPublica -> SaludPublicaService.procesarPregunta
15Everything elsehandleGeneralQuery -> 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 of MAX_MESSAGE_LENGTH = 4000 to leave a small buffer. All responses are passed through sendLongMessage() before being sent:
const MAX_MESSAGE_LENGTH = 4000;

private async sendLongMessage(
  ctx: Context,
  text: string,
  options: { parse_mode?: 'Markdown' | 'MarkdownV2' | 'HTML' } = {},
): Promise<void> {
  if (text.length <= MAX_MESSAGE_LENGTH) {
    await ctx.reply(text, options);
    return;
  }
  // Split on the last newline before the 4000-char boundary
  let currentPosition = 0;
  while (currentPosition < text.length) {
    let endPosition = currentPosition + MAX_MESSAGE_LENGTH;
    if (endPosition < text.length) {
      const lastNewline = text.lastIndexOf('\n', endPosition);
      if (lastNewline > currentPosition) {
        endPosition = lastNewline;
      }
    }
    await ctx.reply(text.substring(currentPosition, endPosition), options);
    currentPosition = endPosition;
  }
}
Splitting on \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:
interface UserState {
  intent: string;
  data?: unknown;
}
private userState = new Map<number, UserState>();
This enables multi-turn flows such as:
  • Location request: user asks for nearby providers -> bot sets intent provider_search_location -> user shares GPS -> @On('location') handler clears the pending intent and calls YopalHealthService.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

ServicePurpose
GenkitServiceOpenRouter / LLaMA 3.1 AI client with exponential-backoff retry
UserServiceTracks per-user greeted state across sessions
StatsServiceAggregates summary context from multiple data sources
SaludPublicaServiceSIVIGILA NLP engine — rankings, comparatives, urban/rural, age/gender breakdowns
SaludPublicaQuestionsServiceIntent classifier for structured public health queries
SaludAnaliticaServiceRAG orchestrator — merges SIVIGILA, vaccination, and air quality into prompts
HealthDataServiceLow-level TypeORM queries on the HealthEvent table
HealthStatsServiceStatistical helpers (ranking, time series projection)
MentalHealthServiceCIE-10 diagnosis search and profile generation
MentalHealthQuestionsServiceIntent router for mental health queries
SexualHealthServiceKeyword search over the sexual health Q&A dataset
AirQualityServiceReads air quality indicators from SQLite by municipality
AirQualityQuestionsServiceFormats air quality capability listings and responses
VaccinationServicePAI vaccination coverage queries by department
YopalHealthServiceProvider search with Haversine distance calculation
YopalQuestionsServiceIntent classifier for Yopal-specific queries
CaliHealthServiceZero-LLM urgency and service detection for Cali providers
AntioquiaHealthServiceProvider search for Antioquia municipalities
AntioquiaQuestionsServiceIntent classifier for Antioquia-specific queries
BoyacaHealthServiceProvider search for Boyaca municipalities
ChartServiceBuilds QuickChart URLs for bar, pie, and line charts
ChartQueryServiceDetects chart intent and dispatches to ChartService
GraphicsQuestionsServiceReturns the catalog of available chart types
PredictiveQuestionsServiceOrchestrates ML scoring, time-series forecasting, and early warnings
MlPredictionServiceComposite multidimensional risk scoring (SIVIGILA + vaccination + rurality)
AdvancedPredictionServiceHolt-Winters-inspired time series decomposition and projection
EarlyWarningServiceThreshold-based automated outbreak alert generation
DatasetBuilderServiceNormalizes and caches data tensors for ML models
PredictionServiceGeneral risk prediction entry point
NationalHealthServiceNational-level aggregated health statistics
IngestaDatosServiceUtility service for data ingestion workflows

Build docs developers (and LLMs) love