M42 Hybrid Forecasting Service - Integration Guide
Last Updated: October 26, 2025 Status: ✅ Ready for Production Integration Milestone: M42 (Complete - All 3 Phases)
Overview
M42 (Hybrid Forecasting Service) is a complete, production-ready forecasting engine that integrates with the Node.js backend. It provides:
- Phase 1: Hybrid Prophet + XGBoost forecasting with walk-forward validation
- Phase 2: External data fusion with FinBERT sentiment analysis
- Phase 3: LLM meta-cognitive gating agent for forecast validation
Architecture
Service Component Structure
┌─────────────────────────────────────────────────────────────────┐
│ Node.js Backend (port 3000) │
│ │
│ HybridForecastingService.js (Enhanced with M42 support) │
│ ├─ Calls /v1/forecast-gated (M42 Phase 3 - default) │
│ ├─ Calls /v1/forecast (M42 Phase 1/2 - fallback) │
│ ├─ Calls /v1/backtest (M42 Phase 1 - validation) │
│ └─ Integrates gating decisions in response │
│ │
└─────────────────┬───────────────────────────────────────────────┘
│
│ HTTP REST (Async, 60-120s timeout)
│
┌─────────────────┴───────────────────────────────────────────────┐
│ Python Forecasting Service (port 8000) │
│ (M42 Complete) │
│ │
│ Main API Endpoints: │
│ ├─ /v1/forecast (Prophet+XGBoost hybrid) │
│ ├─ /v1/backtest (Walk-forward validation) │
│ └─ /v1/forecast-gated (With LLM gating agent) │
│ │
│ Core Modules: │
│ ├─ forecasting_engine.py (Phase 1 - Hybrid model) │
│ ├─ sentiment_engine.py (Phase 2 - FinBERT NLP) │
│ └─ llm_gating_agent.py (Phase 3 - LLM reasoning) │
│ │
└─────────────────────────────────────────────────────────────────┘
Data Flow
Historical Actuals
│
├─→ Convert to TimeSeries (Darts)
│
├─→ Prophet Model (Linear patterns)
│ └─→ Extract Residuals
│
├─→ XGBoost Model (Non-linear residuals + covariates)
│ └─→ Combine Forecasts (Prophet + XGBoost)
│
├─→ Fetch External Data:
│ ├─ News articles (→ FinBERT sentiment)
│ ├─ Weather data
│ └─ Policy events (→ sentiment mapping)
│
├─→ LLM Gating Agent (Meta-cognitive validation)
│ ├─ Heuristic rules (always available)
│ └─ LangChain reasoning (LLM-powered)
│
└─→ Final Forecast + Gating Decision
Integration Setup
1. Prerequisites
Python Service Requirements
- Python 3.9+
- Dependencies in
requirements.txt:fastapi,uvicorn(web framework)pandas,numpy(data processing)darts,prophet,xgboost(time series models)transformers,torch(NLP - FinBERT)requests(HTTP calls)pydantic(data validation)scipy(statistics)
Node.js Backend Updates
- Already has
HybridForecastingService.jsenhanced with M42 support - Calls
/v1/forecast-gatedendpoint by default - Falls back to
/v1/forecastif gating fails - Extracts
gating_decisionfrom response
2. Environment Variables
.env Configuration
# Python Forecasting Service
FORECASTING_SERVICE_URL=http://forecasting-service:8000
# LLM Integration (M42 Phase 3)
GEMINI_API_KEY=<your-gemini-api-key> # For gating agent LLM reasoning
LANGCHAIN_LLM_PROVIDER=gemini # or "openai"
# Optional: OpenAI support for gating agent
OPENAI_API_KEY=<your-openai-api-key>
# Backend API URL (for news/weather/policy data)
BACKEND_URL=http://localhost:3000 # or http://chainalign-backend:3000 in Docker
3. Docker Compose Setup
The docker-compose.yml already includes the forecasting service:
forecasting-service:
build:
context: ./python-services/forecasting_service
ports:
- "8000:8000"
volumes:
- ./python-services/forecasting_service:/app
env_file: .env
environment:
NODE_ENV: development
GEMINI_API_KEY: ${GEMINI_API_KEY}
networks:
- chainalign-net
depends_on:
- chainalign-backend # Needs backend for news/weather/policy APIs
4. Starting the Services
# Start all services including Python forecasting service
docker-compose up -d
# Verify forecasting service is running
curl http://localhost:8000/
# Expected response:
# {"message": "Hybrid Forecasting Service is running!"}
API Endpoints
/v1/forecast - Basic Hybrid Forecast (M42 Phase 1/2)
Method: POST
URL: http://forecasting-service:8000/v1/forecast
Request Body:
{
"time_series": [
{"timestamp": "2023-01-01T00:00:00Z", "value": 100.5},
{"timestamp": "2023-01-02T00:00:00Z", "value": 102.3}
],
"forecast_horizon": 10,
"past_covariates": [
{"timestamp": "2023-01-01T00:00:00Z", "data": {"promo": 1, "weather_temp": 25.5}}
],
"future_covariates": [
{"timestamp": "2023-01-11T00:00:00Z", "data": {"promo": 0, "weather_temp": 22.0}}
],
"future_covariates_are_known": true
}
Response:
{
"forecast": [
{"timestamp": "2023-01-11T00:00:00Z", "value": 115.2}
],
"model_components": {
"prophet_forecast": [...],
"xgboost_residual_forecast": [...]
},
"residuals_for_uncertainty": {
"distribution_type": "empirical",
"statistics": {...},
"appropriate_for_mc": true
},
"model_diagnostics": {...},
"execution_metadata": {
"model_name": "Hybrid Prophet+XGBoost",
"execution_time_ms": 2341.5
}
}
/v1/forecast-gated - Gated Hybrid Forecast with LLM Validation (M42 Phase 3)
Method: POST
URL: http://forecasting-service:8000/v1/forecast-gated
⚠️ DEFAULT ENDPOINT - Used by Node.js backend
Request Body: Same as /v1/forecast
Response:
{
"forecast": {
"forecast": [...],
"model_components": {...},
"residuals_for_uncertainty": {...},
"model_diagnostics": {...},
"execution_metadata": {...}
},
"gating_decision": {
"approve": true,
"confidence": 0.92,
"reason": "Forecast approved (high confidence: 92.0%)",
"flags": [],
"recommendations": ["Monitor inventory levels during forecast period"],
"reasoning_chain": "Heuristic rule-based gating",
"timestamp": "2025-10-26T12:34:56.789Z"
}
}
/v1/backtest - Walk-Forward Validation (M42 Phase 1)
Method: POST
URL: http://forecasting-service:8000/v1/backtest
Request Body:
{
"time_series": [...],
"forecast_horizon": 10,
"backtest_params": {
"strategy": "rolling_window",
"window_size": 30
},
"future_covariates_are_known": true
}
Response:
{
"metrics": {
"mape": 0.0834,
"rmse": 45.23,
"mae": 32.1,
"hybrid_mape": 0.0834,
"prophet_only_mape": 0.1125,
"improvement_percentage": 25.8
},
"backtest_forecasts": [...],
"execution_metadata": {
"model_name": "Hybrid Prophet+XGBoost (Backtested)",
"backtest_strategy": "rolling_window",
"execution_time_ms": 15234.5
}
}
Node.js Backend Integration
Updated HybridForecastingService Flow
-
Calls
/v1/forecast-gated(Primary - M42 Phase 3)- Includes external data context
- Receives both forecast and gating decision
- Logs gating confidence and flags
- Flags low-confidence forecasts for human review
-
Automatic Fallback
- If gated endpoint fails → retries with
/v1/forecast - Graceful degradation ensures service availability
- If gated endpoint fails → retries with
-
Response Enhancement
- Includes
gating_decisionin final response - Sets
requires_human_reviewflag if confidence < 60% - Propagates gating flags and recommendations to frontend
- Includes
Code Integration
// In HybridForecastingService.js
let pythonServiceResponse;
let gatingDecision = null;
let useGatingAgent = true; // M42 Phase 3: Use gating agent by default
try {
// Call Python forecasting service with gating agent
pythonServiceResponse = await callForecastingService(pythonServiceRequestBody, useGatingAgent);
// Extract forecast and gating decision
if (pythonServiceResponse.gating_decision) {
gatingDecision = pythonServiceResponse.gating_decision;
statisticalBaseline = pythonServiceResponse.forecast;
// Flag for human review if confidence is low
if (gatingDecision.confidence < 0.6) {
appLogger.warn(`LOW CONFIDENCE FORECAST - Flagged for human review`);
}
} else if (pythonServiceResponse.forecast) {
statisticalBaseline = pythonServiceResponse.forecast;
}
} catch (error) {
// Fallback logic...
}
Performance & Reliability
Timeouts
- Regular forecast: 60 seconds (model inference)
- Backtest: 120 seconds (walk-forward validation)
- Gated forecast: 60 seconds (includes NLP sentiment + LLM)
Fallback Strategy
/v1/forecast-gated (Primary)
↓ (failure)
/v1/forecast (Fallback)
↓ (failure)
Error response with graceful degradation
Graceful Degradation
- LLM unavailable: Uses heuristic gating agent
- NLP sentiment fails: Proceeds with base forecasts
- External data unavailable: Works with available sources
- Model fails: Returns error to frontend for handling
Monitoring & Logging
Log Levels
INFO Level:
- Forecast request accepted
- Python service endpoint called
- Gating decision approved
- Model diagnostics completed
WARN Level:
- Low confidence forecast (<60%)
- Gating flags detected
- Fallback to regular endpoint
- Missing external data sources
ERROR Level:
- Python service unreachable
- Invalid response format
- Model training failed
- LLM gating failed
Example Logs
[HybridForecastingService] Starting forecast generation for tenant: tenant-123, SKU: SKU-456
[HybridForecastingService] Calling Python forecasting service: http://forecasting-service:8000/v1/forecast-gated
[HybridForecastingService] Hybrid forecast generated with LLM gating validation.
[HybridForecastingService] Gating Decision: approve=true, confidence=0.92, flags=[]
[HybridForecastingService] Forecast generation process completed for tenant: tenant-123
Testing
Local Testing
# Test forecasting service availability
curl http://localhost:8000/
# Test basic forecast endpoint
curl -X POST http://localhost:8000/v1/forecast \
-H "Content-Type: application/json" \
-d '{
"time_series": [...],
"forecast_horizon": 10
}'
# Test gated forecast endpoint
curl -X POST http://localhost:8000/v1/forecast-gated \
-H "Content-Type: application/json" \
-d '{
"time_series": [...],
"forecast_horizon": 10
}'
# Test backtest endpoint
curl -X POST http://localhost:8000/v1/backtest \
-H "Content-Type: application/json" \
-d '{
"time_series": [...],
"forecast_horizon": 10,
"backtest_params": {"strategy": "rolling_window", "window_size": 30}
}'
Unit Tests
# Run Python service tests
cd python-services/forecasting_service
python -m pytest __tests__/ -v
# Run specific test module
python -m pytest __tests__/test_sentiment_engine.py -v
python -m pytest __tests__/test_llm_gating_agent.py -v
Integration Tests
# Docker environment integration test
docker-compose up -d
npm test # From backend directory
Deployment Checklist
- Clone/pull latest code with M42 changes
- Update
.envwithFORECASTING_SERVICE_URL - Update
.envwithGEMINI_API_KEY(for LLM gating) - Build Python forecasting service Docker image
- Verify service starts in Docker:
docker-compose up -d forecasting-service - Test
/v1/forecast-gatedendpoint manually - Verify Node.js backend can call the service
- Check logs for integration success
- Run integration tests:
npm test - Monitor production logs for gating decisions
- Set up alerts for "requires_human_review" flags
Troubleshooting
Issue: "Failed to get forecast from Python service"
Solution:
- Check if forecasting service is running:
docker ps | grep forecasting-service - Verify URL in
.env:FORECASTING_SERVICE_URL - Check Python service logs:
docker logs forecasting-service - Ensure
GEMINI_API_KEYis set for gating agent
Issue: Gating confidence is low (<60%)
Cause: Model diagnostics detected issues (insufficient data, heteroscedasticity, etc.)
Action:
- Review
gating_decision.flagsfor specific issues - Check
gating_decision.recommendationsfor guidance - Flag for human review (automatic in Node.js backend)
Issue: Timeout errors (60-120s)
Solution:
- Check machine resources (CPU, memory)
- Reduce forecast horizon or training data size
- Increase timeout values if needed
- Check for network latency
Issue: LLM gating agent failing
Solution:
- Verify
GEMINI_API_KEYorOPENAI_API_KEYis set - Check LLM API quota/limits
- Service falls back to heuristic gating (still functional)
- Check Python service logs for LangChain errors
Next Steps: M43
With M42 fully integrated and production-ready, the next milestone (M43) will implement:
- Phase 0: Database migrations & repositories
- Phase 1: Python bootstrap service
- Phase 2: Adaptive forecasting services
- Phase 3: Startup initialization service
- Phase 4: E2E testing and documentation
The M42 foundation provides the forecasting backbone that M43 will enhance with adaptive parameters and bootstrap logic.
Support & Documentation
- Python Service API:
python-services/forecasting_service/README.md - Detailed Architecture:
/docs/worksummary/milestone-42/summary.md - API Specification: Pydantic models in
models.py - Testing Guide:
/docs/testing/TDD Summary.md