Skip to content

Machine learning ​

CoCoCo lets you train machine-learning models on your own tenant data and call them from the API, from workflows and from Lua scripts. A model is an ML model config (MLModelConfigOutput). It says where the training data comes from, what to learn and which engine to use. In the console, models are listed under AI & ML → ML Models.

Model kinds ​

Model type (MLModelType)PredictsEngine (MLBackend)Spec
REGRESSIONA numberXGBOOST (gradient-boosted trees)TabularModelSpec
CLASSIFICATIONA category label and a confidenceXGBOOSTTabularModelSpec
FORECASTFuture values of a series over a horizonAUGURSTimeSeriesModelSpec

The platform derives modelType from the spec you provide: a tabular spec with task: REGRESSION or CLASSIFICATION, or a time-series spec (always FORECAST). If you omit backend, the default engine for that spec is used.

Forecast models use one of three algorithms (TimeSeriesModelKind): ETS (automatic exponential smoothing), MSTL (multiple seasonal-trend decomposition) or PROPHET (trend plus seasonality). MSTL needs seasonalPeriods, and PROPHET needs a time column whose every value parses as a date. A series needs at least three rows.

Training data ​

A model trains from one of two sources:

  • An uploaded CSV file (up to 10 MB), added in the console's model form. When a CSV is present it takes precedence over the SQL query. Set clearTrainingCSV: true on an update to remove it.
  • dataQuerySQL, a MicroSQL query over your tenant's reporting tables. It runs again each time the model trains, so retraining picks up new rows. Qualify every column with its table (progress_points.speed), filter only on indexed columns, and give each selected column an alias: the spec refers to columns by the name the query returns. The describeQuerySchema query checks a query and shows the column names it returns.

Tabular models can also have normalizationStages: an ordered list of pure-Lua stages (no host APIs) that each read the global rows and return transformed rows. Use them for cleaning, quality checks or feature engineering. previewMLModelPipeline runs the pipeline over a sample without training. It returns the emitted columns and any type-check diagnostics for each stage.

Define a model ​

Tabular models name a targetColumn and one or more featureColumns from the data source. Hyperparameters (MLHyperparametersInput) are optional. Names start with a lowercase letter and may contain lowercase letters, digits, hyphens and underscores (up to 64 characters).

graphql
mutation CreateWastePredictor {
  createMLModelConfig(input: {
    name: "waste-predictor"
    description: "Predicts waste per production run"
    dataQuerySQL: "SELECT progress_points.speed AS speed, progress_points.total_planned AS total_planned, progress_points.phase_duration_seconds AS duration_seconds, progress_points.waste_count AS waste_count FROM progress_points WHERE progress_points.phase = 'RUNNING'"
    spec: {
      tabular: {
        task: REGRESSION
        targetColumn: "waste_count"
        featureColumns: ["speed", "total_planned", "duration_seconds"]
        hyperparameters: { maxDepth: 6, rounds: 200 }
      }
    }
  }) {
    mlModelConfig { id name modelType backend status }
    errors { path message }
  }
}

Forecast models name a timeColumn (dates or numbers that order the rows) and a valueColumn. You can also set seasonal periods, a default horizon and a default confidence level.

graphql
mutation CreateDemandForecast {
  createMLModelConfig(input: {
    name: "weekly-demand"
    dataQuerySQL: "SELECT progress_points.timestamp AS recorded_at, progress_points.good_count AS good_count FROM progress_points ORDER BY progress_points.timestamp ASC"
    spec: {
      timeSeries: {
        model: MSTL
        timeColumn: "recorded_at"
        valueColumn: "good_count"
        seasonalPeriods: [52]
        forecastHorizon: 12
        confidenceLevel: 0.9
      }
    }
  }) {
    mlModelConfig { id modelType backend }
    errors { path message }
  }
}

The table and column names in these examples are placeholders. Use columns that exist in your own data.

Model configs can also be exported and imported as YAML or JSON with exportMLModelConfig and importMLModelConfig.

Train and check status ​

Training runs in the background. trainMLModel sets the status to TRAINING and returns immediately.

graphql
mutation Train($id: MLModelConfigID!) {
  trainMLModel(id: $id) {
    mlModelConfig { id status }
    errors { path message }
  }
}

Poll the config until status is TRAINED or FAILED (MLModelTrainingStatus):

graphql
query ModelStatus($id: MLModelConfigID!) {
  getMLModelConfig(id: $id) {
    name
    status
    lastTrainedAt
    trainingDurationMs
    trainingError
    trainingMetrics { rmse mae r2 accuracy logloss }
    featureImportance { feature gain rank }
    publishedVersionNumber
  }
}

Regression runs report rmse, mae, maxError and r2. Classification runs report accuracy, plus logloss for two-class models. Tabular metrics are computed on a sample of the training rows, not on held-out data, so treat them as an optimistic estimate. Forecast runs report rmse and mae from forecasting a held-out tail of the series (left empty when the series is too short). Tabular models also report featureImportance. After training, inferenceSchema holds a JSON Schema that describes the input features the model expects.

Versions ​

Every successful training run creates an immutable version (MLModelConfigVersionOutput) with its own metrics and row count. With autoPublish on (the default), each new version is published automatically. To publish a specific version yourself, use publishMLModelVersion.

graphql
query ModelVersions($id: MLModelConfigID!) {
  listMLModelConfigVersions(configId: $id, first: 10) {
    edges {
      node { id version trainingRowCount trainingMetrics { rmse r2 } createdAt }
    }
  }
}

Predict ​

A model must be TRAINED before it can predict.

From GraphQL ​

predictMLModel takes the feature values as a JSON string. It returns a MLModelPrediction: prediction for regression, predictedClass and confidence for classification, and per-feature contributions. Missing features or type mismatches are reported in warnings. They do not fail the call.

graphql
mutation Predict($id: MLModelConfigID!) {
  predictMLModel(
    id: $id
    input: "{\"job_quantity\": 5000, \"substrate_weight_gsm\": 170, \"substrate_coating\": \"gloss\"}"
  ) {
    prediction { prediction predictedClass confidence warnings contributions { feature contribution } }
    errors { path message }
  }
}

Forecast models use forecastMLModel instead. It returns a point series for the horizon, plus lower and upper bounds when a confidence level is given or the model has a default one.

graphql
mutation Forecast($id: MLModelConfigID!) {
  forecastMLModel(id: $id, horizon: 12, level: 0.9) {
    forecast { point lower upper level }
    errors { path message }
  }
}

From a workflow ​

The ml_predict workflow node runs a model by modelName:

  • Tabular models take features, an object whose values can be literals or {{ … }} expressions such as {{ input.quantity }}. The node outputs { prediction } for regression and { predictedClass, confidence } for classification.
  • Forecast models take no features. Optional horizon and confidenceLevel settings override the model's defaults. The node outputs { forecast: [{ step, point, lower?, upper? }], level? }.

See Workflow automation for how nodes are wired together.

From Lua ​

In Lua scripts that run on the platform (scripts, custom app server code and integrations), ctx.ml.get(name) returns a model object with predict, forecast and info methods. Each call returns ok, result.

lua
-- Tabular model: predict from a feature table
local waste = ctx.ml.get("waste-predictor")
local ok, result = waste:predict({
  job_quantity = 5000,
  substrate_weight_gsm = 170,
  substrate_coating = "gloss",
})
if not ok then error(result) end
if result.warnings then
  for _, w in ipairs(result.warnings) do
    ctx.log.warn(w)
  end
end
ctx.log.info("predicted waste ratio: " .. tostring(result.prediction))

-- Forecast model: 14 steps ahead with a 90% interval
local demand = ctx.ml.get("weekly-demand")
local infoOk, info = demand:info()
if not infoOk then error(info) end
if info.status == "TRAINED" then
  local fcOk, fc = demand:forecast(14, 0.9)
  if not fcOk then error(fc) end
  local lower = fc.lower or {}
  for i, point in ipairs(fc.point) do
    ctx.log.info(string.format("step %d: %.1f (lower %s)", i, point, tostring(lower[i])))
  end
end

ctx.ml.predict(modelName, featuresJSON) is a shorter form for tabular models that takes the features as a JSON string. ctx.ml also has stateless time-series helpers that need no trained model: detectChangepoints, detectOutliers, detectSeasonality, interpolate, dtwDistance, dtwDistanceMatrix and clusterSeries. See the Lua API overview.

System models ​

Some models are created and retrained by the platform itself. These have isSystem: true and names that start with __system_, for example __system_waste_predictor and __system_duration_predictor. To list only your own models, filter with isSystem: false:

graphql
query MyModels {
  listMLModelConfigs(first: 20, filter: { isSystem: false }) {
    totalCount
    edges { node { id name modelType backend status lastTrainedAt } }
  }
}

Permissions ​

Reading models needs mlmodelconfig:read (listing needs mlmodelconfig:list). Creating and changing models needs mlmodelconfig:write, and deleting them needs mlmodelconfig:delete. Training, previewing, predicting and forecasting need mlmodelconfig:execute.

Connect machines, automate workflows, and build custom apps for print production.