Prediction
When training has successfully run for a dataset, everything is ready for generating predictions. The Inventory Optimization API offers two prediction processes, one asynchronous process and one synchronous. The async process (Create Prediction) is called when the client wants to generate both forecasts and purchase order suggestions for one or more datasets. When this process runs, the results are fetched through a separate endpoint, called Results.
However, in a human-in-the-loop AI purchase order process, the end-user will want to overwrite the forecasts if certain values are inaccurate and the user knows better. In this case, the purchase order suggestions will be updated, as the forecast is an important input to their calculation. Therefore, we have implemented the Real-Time Prediction endpoint, where clients can send in forecasts as input, and get updated purchase order suggestions in a few seconds. Note that the forecasts are provided by the client when calling the Real-Time Prediction endpoint.
The rest of this page outlines the details of running the two prediction processes; Create Prediction (async) and Real-Time Prediction (sync). The error messages that clients can encounter are outlined at the end.
Create Prediction
When running the prediction process asynchronously with Create Prediction, clients first start a job, wait for it to be done, then fetch the results from the Result endpoint.
Starting a Create Prediction Job
Calling the Create Prediction endpoint starts a prediction job. The prediction job generates forecasts and suggestions for each dataset defined in the payload. Each dataset's future demand is forecasted with its trained model. Then, the Product Replenishment Problem is solved with the forecast and planner parameters as input. These parameters are set in the payload of the request to the Create Prediction endpoint. See here for details about the Product Replenishment Problem and definitions of the parameters.
An example of a set of input parameters for a Create Prediction job.
{
"datasetId": "f1c78da657c3",
"currentInventoryLevel": 20.0,
"wantedServiceLevel": 0.95,
"minimumInventory": 10.0,
"maximumInventory": 100.0,
"inventoryOrders": [
{
"estimatedDeliveryDate": "2024-03-21",
"quantity": 15.0
}
],
"futureSalesOrders": [
{
"departureDate": "2024-03-22",
"quantity": 5.0
}
],
"replenishmentInterval": {
"value": 4,
"granularity": "W"
},
"supplier": {
"supplierId": "c7286ec361e7",
"leadTime": {
"value": 1,
"granularity": "W"
}
}
}
Create Prediction Parameters
datasetId required | string The unique dataset ID (unique product) to run prediction for. |
currentInventoryLevel required | number >= 0 The current stock level in the inventory. |
wantedServiceLevel required | number [ 0.5 .. 0.99 ] The planner's desired availability of the product, expressed as a percentage. |
required | object (Supplier) Information about a supplier of a product. |
object (Planning Period) The number of days, weeks, or months the replensihment should cover demand for. The
planning period must be longer than the lead time. At least one of
| |
Array of objects (Inventory Orders) Array with incoming inventory orders of the product. | |
Array of objects (Future Sales Orders) Array with future sales orders of the product. | |
minimumInventory | number >= 0 The minimum quantity of a product that the customer wants on stock. This field should only be set, if there are physical limitations to the minimum stock level. |
maximumInventory | number >= 0 The maximum quantity of a product that the customer wants on stock. This field should only be set, if there are physical limitations to the maximum stock level. |
object (Replenishment Interval) Will be outdated by the end of summer 2024 - use the |
[- {
- "datasetId": "string",
- "currentInventoryLevel": 0,
- "wantedServiceLevel": 0.5,
- "supplier": {
- "supplierId": "string",
- "batchSize": 0,
- "unitPrice": 0,
- "leadTime": {
- "value": 0,
- "granularity": "D"
}
}, - "planningPeriod": {
- "value": 1,
- "granularity": "D"
}, - "inventoryOrders": [
- {
- "estimatedDeliveryDate": "string",
- "quantity": 0
}
], - "futureSalesOrders": [
- {
- "departureDate": "string",
- "quantity": 0
}
], - "minimumInventory": 0,
- "maximumInventory": 0,
- "replenishmentInterval": {
- "value": 1,
- "granularity": "D"
}
}
]
Fetching Suggestions from Results
When the Create Prediction job has finished running, the results are fecthed from the
Results endpoint. The below shows an example of suggestions returned from the API. The
validDateInterval
fields defines the start and end date for which the suggestions are
valid. Note that the startDate
defines the date we assume the user places the purchase
order for the product. This is always equal to the current date. The endDate
is the
end of the planning period.
An example of suggestions from the Results endpoint.
{
"datasetId": "f1c78da657c3",
"supplierId": "c7286ec361e7",
"replenishmentSuggestion": {
"quantity": 45.0,
"validDateInterval": {
"startDate": "2024-03-20",
"endDate": "2024-04-10"
}
},
"safetyStockSuggestion": {
"quantity": 10.0,
"validDateInterval": {
"startDate": "2024-03-20",
"endDate": "2024-04-10"
}
},
"reorderPointSuggestion": {
"quantity": 20.0,
"validDateInterval": {
"startDate": "2024-03-20",
"endDate": "2024-04-10"
}
}
}
Replenishment Suggestion
quantity required | number The suggested quantity to order to cover planning period demand. |
required | object (Valid Date Interval) The date interval for which this suggestion is valid for. |
{- "quantity": 0,
- "validDateInterval": {
- "startDate": "string",
- "endDate": "string"
}
}
Safety Stock Suggestion
quantity required | number The suggested quantity to keep as safety stock. |
required | object (Valid Date Interval) The date interval for which this suggestion is valid for. |
{- "quantity": 0,
- "validDateInterval": {
- "startDate": "string",
- "endDate": "string"
}
}
Reorder Point Suggestion
quantity required | number The suggested inventory level at which to trigger a purchase order. |
required | object (Valid Date Interval) The date interval for which this suggestion is valid for. |
{- "quantity": 0,
- "validDateInterval": {
- "startDate": "string",
- "endDate": "string"
}
}
Fetching Forecasts and Historical Data from Results
The forecast and historical data that was used to generate these are also fetched from
the Results endpoint after the prediction process has run. The returned historical sales
data is simply the given sales data aggregated in the same granularity as the returned
forecast. This is determined by the frequency
parameter set when the Start Trainer
endpoint was called to find the best model for this dataset.
An example of parts of a forecast and historical data from the Results endpoint.
{
"forecast": [
{
"date": "2024-03-18",
"predictedQuantity": 50.0,
"predictedSeason": 23.33,
"predictedTrend": 16.67,
"predictedNoise": 20.0,
"lowerQuantity": 30.0,
"upperQuantity": 70.0,
},
...
],
"historicalData": [
{
"date": "2024-02-16",
"quantity": 40.0
},
{
"date": "2024-01-14",
"quantity": 30.0
},
...
]
}
Forecast
date required | string The date of a forecast data point. |
predictedQuantity required | number The quantity of the product forecasted to be sold at the given date. |
predictedSeason required | number The part of the forecast that originates from the season. |
predictedTrend required | number The part of the forecast that originates from the trend. |
predictedNoise required | number The part of the forecast that originates from the noise. |
lowerQuantity required | number The lower bound of the forecast interval for the forecasted quantity. |
upperQuantity required | number The upper bound of the forecast interval for the forecasted quantity. |
[- {
- "date": "string",
- "predictedQuantity": 0,
- "predictedSeason": 0,
- "predictedTrend": 0,
- "predictedNoise": 0,
- "lowerQuantity": 0,
- "upperQuantity": 0
}
]
Historical Data
date required | string^\d{4}-\d{2}-\d{2}$ The date of the historical sales data point. Date will be in format 'YYYY-MM-DD'. |
quantity required | number >= 0 The quantity of the product sold at the given date. |
[- {
- "date": "string",
- "quantity": 0
}
]
Step-by-step instructions: Create Prediction
- Determine the tenant and dataset(s) to create predictions for.
- Create the parametersArray with parameters for each dataset.
- Send a
POST
request to /create_prediction following the schema in the API reference. - The endpoint will return a jobId. - Do one of the following...
- Call GET /status with the tenantId and jobId in the header until the status is “success”.
- Provide a webhook in the body of the PUT request in step 3 to receive a request when the job is finished running.
- If the job status is "success", send a
GET
request to the /result endpoint with the jobId in the header to fetch the predictions.
Real-Time Prediction
Interacting with the Real-Time Prediction endpoint is similar to the interaction with the Create Prediction and Results endpoint described above. The difference is that the client provides the forecast for each stock item in the request payload, and that results are returned in real-time after a few seconds.
Step-by-step instructions: Real-Time Prediction
- Determine the tenant and dataset(s) to create predictions for.
- Create the parametersArray with parameters for each dataset - now with the forecast values.
- Send a
POST
request to /real_time_prediction following the schema in the API reference. - The endpoint returns the results.
Error Messages
Error Code | Error message | Action | Example |
---|---|---|---|
SUCCESS | Training succeeded. | Job succeeded. | |
NO_DATA | No data has been uploaded for the dataset. | Check that data has been uploaded for the dataset. | |
INSUFFICIENT_DATA | Insufficient data for the prediction. | Check that the dataset has enough data for the prediction. | |
NO_MODEL | Model missing for dataset. | Check that training has been run for the dataset. | |
INVALID_MODEL | Model is invalid or not found - retry training and prediction. | Check that the model is valid and retrain if needed. | |
INVALID_INVENTORY_ORDERS | Inventory order date is invalid or not in the future. | Check that the inventory orders have valid dates. | |
FUTURE_SALES_ORDER | Sales order date is invalid or not in the future. | Check that the futureSalesOrders have valid date formats YYYY-MM-DD and that they are on or after the current date. Consider changing freezeDate if given. | futureSalesOrders: [{ "departureDate": "2024-15-22", "quantity": 5.0}] |
INVALID_FREEZE_DATE | Freeze date is invalid or in the future. | Check that the freeze date is valid has a valid date format YYYY-MM-DD and that it is not after the current date. Freeze date should be use only if you want to make predictions in the past. | freezeDate = 2022-01-32 or freezeDate = 2072-01-31 |
INVALID_MIN_MAX_INVENTORY | Min inventory is greater than max inventory. | Check that the minInventory is less than the maxInventory. | minInventory =100, maxInventory = 75 |
PP_EXCEEDS_HORIZON | Length of planning period + one frequency length exceeds forecast horizon | To fix this, either decrease the length of the planning period or increase the horizon. The requirements for the relation between the two is: Length of planning period in days + 1 forecast frequency in days < horizon in days . | Planning period = 6 weeks, horizon = 2 months. This gives: 6*7 + 30 = 72 which is larger than the horizon in days (60). |
LT_GREATER_THAN_PP | Lead time is greater than or equal to planning period | The planning period has to be strictly larger than the lead time. Increase the planning period or decrease the lead time. | leadTime = {"value": 3, "granularity": "W"} , planningPeriod= {"value": 20, "granularity": "D"} |
Specifically for forecast in the real-time prediction | |||
FORECAST_DATE_INVALID | Forecast date is invalid or not in the future. | Check that the forecast objects have valid date formats YYYY-MM-DD and that they are on or after the current date. Consider changing the freezeDate if given. | "forecast": [{ "date": "1921-03-08", "predictedQuantity": 50.0,}, {"date": "2023-03-81", "predictedQuantity": 10.0,}] |
FORECAST_INVALID_FREQUENCY | Forecast does not match a frequency | Check that the forecast follow a frequency. This can be one datapoint per day, per week or per month. | "forecast": [ {"date": "2024-03-08", "predictedQuantity": 50.0,}, {"date": "2024-03-11", "predictedQuantity": 10.0,}] |
FORECAST_WRONG_FREQUENCY | Forecast frequency does not match frequency from model | The most recently trained model for this datasetId does not have the same frequency as the forecast provided. These need to be aligned. | Model is trained weekly while "forecast": [ {"date": "2024-03-31", "predictedQuantity": 50.0,}, {"date": "2024-04-30", "predictedQuantity": 10.0,}] |
FORECAST_TOO_SHORT | Forecast is too short, the planning period exceeds the end of the forecast | The forecast does not cover the full planning period. To fix this, decrease the length of the planning period or increase the forecast. | Planning period is 28 days while "forecast": [ {"date": "2024-04-17", "predictedQuantity": 50.0,}, {"date": "2024-04-24", "predictedQuantity": 10.0,}] |
FORECAST_TOO_FAR_IN_FUTURE | Forecast is too far in the future, there is no forecast for the planning period | The given forecast is too far in the future. Check that this is right, and consider changing the freezeDate if given. | "forecast": [ {"date": "2065-04-30", "predictedQuantity": 50.0,}, {"date": "2065-05-31", "predictedQuantity": 10.0,}] |