Essential medicines · 4 to 8 weeks ahead

Know which clinic runs out before it does.

StockSense reads monthly LMIS reports, predicts which primary health care facilities will run out of ACTs, ORS + zinc or oxytocin, works out how much to send, and drafts the resupply memo. The model does the counting. The AI only explains, and every number it writes is checked against the data.

Open source, MITOne command to runEvery number traced to a row
8-week risk landscapedata to …SYNTHETIC
–pairs at high risk
–facilities affected
–units planned
Very highHighWatchLowheight = risk

    Resupply memo draft

    GUARD PASSED
    Drag to orbit · click a column

    Tracks the medicines that cannot run out

    The problem

    A stockout is usually visible weeks before it happens. The shelf is getting thin, the rains are coming, the last delivery was short, and the facility stopped reporting. But those signals sit in different spreadsheets, and the monthly report only says what was dispensed, not what patients needed.

    Hidden demand

    An empty shelf makes demand look low.

    When a clinic is out of ACTs for 20 days, the report shows 20 days of dispensing. Plan from that and the next delivery is short too. StockSense corrects for the days out of stock and flags it.

    Seasonal surge

    Malaria doesn't wait for the next requisition.

    ACT and RDT use climbs with the rainy season. A fixed months-of-stock rule notices after shelves are bare. The model learns each facility's seasonal pattern and plans life-saving items to the high forecast.

    Reporting gaps

    Silence is a signal.

    Facilities that skip reports are often the ones in trouble. Reporting rate is a model feature and a reason code.

    Bad numbers

    Opening + received − used ≠ closing.

    Balance errors, impossible stockout days and outliers are flagged before a single forecast is made.

    Blind push

    Everyone gets the same top-up.

    Without a ranked plan, scarce stock goes to whoever asked first. StockSense ranks by risk × how critical the item is.

    How it works

    Report in. Plan out. Every Monday.

    One scoring job, cron-ready, turns last month's reports into a ranked resupply plan and a memo a logistics officer can sign.

    01

    Check the reports

    DHIS2 or LMIS exports are validated, mapped to the facility registry and checked: balance equation, gaps, outliers. Stockout months are corrected for hidden demand.

    panderaDQ flags
    02

    Forecast the risk

    LightGBM forecasts monthly use as a range (p10, p50, p90) and a calibrated classifier gives the chance of a stockout in 4 and 8 weeks.

    quantilesisotonicSHAP
    03

    Plan the resupply

    A min-max policy works out how much to send, ranked by risk × criticality, within the budget or truck space you set. Each line says why.

    min-maxPuLP / HiGHS
    04

    Explain and check

    The AI writes the memo from a fact pack of pre-computed numbers. A number guard rejects anything it can't trace to a row.

    Structured Outputsnumber guard
    Live outlook

    The current forecast, straight from the model.

    These are the numbers the scoring job produced: 8-week stockout risk for every facility and medicine, and the resupply plan. Hover a facility to see its riskiest item.

    Loading…SYNTHETIC DATA · SIMULATED FACILITIES
    –facility × medicine pairs scored
    –at high risk (50% or more)
    –facilities with a high-risk item
    –units in the resupply plan

    Facilities by highest 8-week risk

    High-risk pairs by LGA

    By medicine

    Highest-risk facility and medicine pairs
    FacilityMedicineRiskWeeks leftSendWhy
    Model lab

    Change the inputs. Watch the model think.

    This runs the trained model itself, not an animation of it. Pick a facility, move a slider, and the real LightGBM models re-score it: calibrated risk, forecast range, the optimiser's order, every input's contribution, and the path through an actual decision tree. Nothing you do here is saved.

    Waiting for data…SYNTHETIC DATA

    Loading…

    The tick under each slider marks what the facility actually reported. Inputs you don't touch keep their real values, as do the other 25 inputs the model reads.

    waiting
    –chance of a stockout within 8 weeks
    –
    Within 4 weeks–
    Weeks of stock–
    Monthly use forecast (p10 – p50 – p90)
    4 wk
    –
    8 wk
    –
    Optimiser says send
    –

    Why: every input's push

    SHAP values for the 8-week classifier. Red pushes the risk up, green pulls it down. They add up exactly to the model's score.

    raises risklowers risk

    Inside one tree

    The path this facility takes

    Why a facility is flagged

    One clinic, one medicine, the whole story.

    History, the forecast range and the three strongest reasons, the same view a PHC coordinator gets in the dashboard.

    Monthly consumption

    Reported use Corrected demand Forecast p10–p90 Forecast p50
    Show as a table

    Top reasons (SHAP)

    What pushed this prediction up or down the most.

    corrected = used × days_in_month
              ÷ (days_in_month − days_out)

    Months out of stock all month are left unknown, not guessed.

    See it react

    Five ways stock runs out. Five things StockSense does.

    The number guard

    The model counts. The AI explains. The guard checks.

    Language models are good at writing and bad at arithmetic. So StockSense never lets one compute a quantity. It writes from a fact pack where every number is already formatted by code, cites the facts it uses, and a guard rejects the draft if any number can't be traced.

    Code

    Computes every number

    Risks, weeks of stock and quantities come from the model and optimiser, formatted once, with the source row recorded.

    AI

    Explains them

    OpenAI Structured Outputs write the headline, risks, actions and caveats, citing fact IDs. No quantity is calculated in a prompt.

    Guard

    Rejects what doesn't trace

    One retry with the guard's feedback, then a plain template. Across 40 test memos, 100% passed.

    Fact pack click to cite

    Try to sneak a number past it

      in a cited factin the pack, wrong factnot in the packname ignored (entity)
      Resupply calculator

      Work out an order the way StockSense does.

      Type one facility's numbers. The calculator uses the same correction and min-max rule as the optimiser. Where the app uses the model's forecast for monthly need, here you type your own estimate. It runs in your browser; nothing is sent.

      From the LMIS report.
      0 if never out.
      Leave empty to use the corrected demand.
      Recommended order
      –

      Results

      Tested on 12 months it had never seen.

      Rolling-origin backtest: at each of 12 past months the model was trained only on data up to that month, then scored on what happened next. Compared against the two rules a logistics team would use anyway.

      Forecast error (MASE, lower is better)

      1.0 means as good as last month's value. Below 1.0 beats it.

      ✓
      Stockout-days avoided –

      Will it run out? Classifier vs months-of-stock rule

      AUROC: ranking quality (higher is better). Brier: probability error (lower is better). Recall @ top 10%: share of real stockouts caught if you only check the riskiest tenth.

      Are the probabilities honest?

      Calibration: when it says 30%, about 30% should run out. Closer to the diagonal is better.

      4 weeks8 weeksperfect

      Stockout facility-days, last 6 months (simulated replay)

      Same history, three ways of deciding what to send.

      Memo number guard –

      Read these honestly. All results are on synthetic data from the built-in simulator, which encodes our own assumptions about seasonality, shocks and reporting. A model that does well here may do worse on real LMIS data. Re-run make eval on your own data and read the model card before relying on it.

      Who it's for

      Built around two questions.

      State LMCU · logistics officer
      “Which facilities will run out of ACTs or ORS + zinc next month, and how much should I send?”
      • Risk map and table, filtered by LGA and medicine
      • A ranked resupply plan with reasons on every line
      • A memo to sign, exported to Word or PDF, with citations
      • Budget and truck-capacity limits when stock is short
      PHC coordinator
      “Why is my facility flagged?”
      • Consumption history and a forecast fan chart
      • The three strongest reasons, in plain words
      • Data-quality flags to fix at the next report
      • Ask a question; the answer cites the rows it used
      Open source

      One command. Your own servers.

      Clone it and start the stack: Postgres, the API, the dashboard and this site. The first start generates synthetic data, runs the backtest and trains the model. Then load your DHIS2 export and retrain.

      docker compose up --build
      FastAPIPostgreSQLLightGBMSHAPPuLP + HiGHSOpenAI SDKStreamlitDocker ComposeGitHub Actions
      stocksense — first start
      
          
      Trust

      Honest about what it knows.

      Synthetic is always labelled

      Simulated data is marked in the database, the dashboard, the memo and this page. It is never shown as real.

      Every number has a source

      Each memo stores its fact pack, and each fact records the predictions or plan row it came from.

      People decide

      The plan and memo are drafts for a logistics officer to check. Data-quality caveats are part of every memo.

      Your data stays yours

      Self-hosted. Stock reports hold no patient data. The LLM is optional and only sees the fact pack.

      Measured, not claimed

      Backtest metrics against simple baselines ship with every model run, with a calibration plot on the model card.

      Limits written down

      Monthly data only, no in-transit stock in standard exports, and a model that must be retrained as supply practice changes.

      FAQ

      Plain answers.

      Does the AI decide how much stock to send?+

      No. Quantities come from a min-max inventory rule applied to the model's forecast, and optionally a linear programme when budget or truck space is limited. The AI only writes the explanation, and the number guard rejects any number it did not copy from the fact pack.

      Is this real data?+

      Not in the demo. The demo runs on a simulator that invents 300 facilities across Enugu State's 17 LGAs, with IDs starting SYN-. Every synthetic row is labelled, and the site and dashboard show a SYNTHETIC banner whenever it is in use.

      How do I plug in our DHIS2 export?+

      Load the facility registry, then monthly reports with facility, period, commodity, opening balance, received, consumed, losses, closing balance and days out of stock. Common DHIS2 headers are recognised and commodity names are mapped through aliases in a config file. Then train and score. Start from an empty database so synthetic and real data never mix.

      What does "4 to 8 weeks ahead" mean with monthly reports?+

      Reports are monthly, so the 4-week horizon is next month and the 8-week horizon is the month after. Deliveries within a month are not modelled.

      Do we need an OpenAI key?+

      No. Without a key, a deterministic template writes the memo from the same fact pack, and the same guard checks it. With a key, the model set in OPENAI_MODEL writes it using Structured Outputs.

      Which medicines does it track?+

      ACTs, malaria RDTs, ORS + zinc, amoxicillin DT, oxytocin, family planning implants and iron-folate. The list, units, min and max months and lead times live in one config file you can edit.

      What does it cost?+

      Nothing. It is MIT-licensed open source. You pay only for your own hosting and, if you choose, OpenAI API usage.

      Start this week

      Send the stock before the shelf is empty.