Predict.aiDocs
Examples

CSV to live forecast

The full production loop — load history from a CSV, define a segment, train a model, deploy the winner, serve forecasts.

This is the canonical predictAI story, end to end: two years of daily sales history goes in as a CSV, and a continuously improving forecast comes out over HTTP. Every block builds on the previous one — run them top to bottom.

You'll need: an API token and a CSV of daily history with columns date,sales,foot_traffic,promo. No CSV handy? The first block generates a realistic one.

0. Demo data (skip if you have a CSV)

import csv, math, random
from datetime import date, timedelta

random.seed(42)
start = date.today() - timedelta(days=730)

with open("daily_sales.csv", "w", newline="") as f:
    w = csv.writer(f)
    w.writerow(["date", "sales", "foot_traffic", "promo"])
    for i in range(730):
        d = start + timedelta(days=i)
        promo = 1 if random.random() < 0.15 else 0
        season = 1 + 0.25 * math.sin(2 * math.pi * i / 365)
        weekday = 1.15 if d.weekday() < 5 else 0.8
        traffic = int(1800 * season * weekday * random.uniform(0.9, 1.1))
        sales = round(traffic * (7.0 + 1.5 * promo) * random.uniform(0.95, 1.05), 2)
        w.writerow([d.isoformat(), sales, traffic, promo])

1. Workspace

from predictai import PredictAI

client = PredictAI(token="pa_live_...")

workspace = client.workspaces.post_workspace(json={
    "name": "Coffee chain demand",
    "goal": "Forecast daily sales 14 days out",
    "data_granularity": "day",
})
WORKSPACE_ID = workspace["data"]["uid"]
client.workspace_id = WORKSPACE_ID

2. CSV in — signals out

Each CSV column becomes a signal; each row, one value per signal:

import csv

with open("daily_sales.csv") as f:
    for row in csv.DictReader(f):
        ts = f"{row['date']}T00:00:00Z"
        client.signals.post_signal_push(json={"key": "daily_sales",  "value": float(row["sales"]),        "timestamp": ts})
        client.signals.post_signal_push(json={"key": "foot_traffic", "value": float(row["foot_traffic"]), "timestamp": ts})
        client.signals.post_signal_push(json={"key": "promo_active", "value": int(row["promo"]),          "timestamp": ts})

For big files, skip the loop: the ingestion API uploads the whole CSV in one call and maps columns to signals server-side.

3. The segment — your training table

A segment pins down exactly what a model trains on: which columns, on what time grid, with gaps filled how. Training and serving both read from it, so the model always sees the same data shape:

segment = client.segments.post_segment(json={
    "name": "Daily sales view",
    "workspace_id": WORKSPACE_ID,
    "features": ["foot_traffic", "promo_active", "daily_sales"],
    "labels": ["daily_sales"],
    "interval": 86400,
    "tolerance": 43200,
    "live": True,
})
SEGMENT_ID = segment["data"]["uid"]

4. A model and a pipeline

The model describes an architecture (this LSTM is the documented reference — input units matches the segment's three features). The pipeline binds it to the segment; with no schedule set, one training run queues immediately:

model = client.models.post_models(json={
    "kind": "custom",
    "name": "LSTM demand forecaster",
    "purpose": "Forecast daily sales from store drivers",
    "description": "Stacked LSTM for daily demand forecasting",
    "visibility": "private",
    "categories": ["retail"],
    "tags": ["demand"],
    "metadata": {"framework": "TensorFlow", "framework_version": "2.x", "training_environment": "cloud"},
    "model_blueprint": {
        "model_type": "sequential",
        "layers": [
            {"type": "Input", "units": 3},
            {"type": "LSTM", "units": 64, "activation": "tanh", "return_sequences": False},
            {"type": "Dropout", "rate": 0.2},
            {"type": "Dense", "units": 1, "activation": "linear"},
        ],
    },
    "compilation_details": {
        "loss": "huber",
        "optimizer": {"type": "adamw", "learning_rate": 0.001},
        "metrics": ["mae", "rmse"],
    },
    "training_details": {"epochs": 200, "batch_size": 64, "sequence_length": 20},
})
MODEL_ID = model["data"]["uid"]

pipeline = client.pipelines.post_pipelines(json={
    "name": "Daily sales forecaster",
    "segment": SEGMENT_ID,
    "workspace_id": WORKSPACE_ID,
    "models": [{"model": MODEL_ID}],
    "forecast_horizon": 14,
    "promotion_policy": {"mode": "auto"},
})
PIPELINE_ID = pipeline["data"]["uid"]

5. Wait for training and deployment

The run takes a few minutes. promotion_policy: auto means the winner deploys itself — wait for both:

import time

while True:
    runs = client.trainings.get_training(params={"model_id": PIPELINE_ID, "per_page": 1})
    latest = runs["data"]["trainings"][0]
    print("training:", latest["last_status"])
    if latest["last_status"] in ("completed", "failed"):
        break
    time.sleep(30)

while True:
    status = client.deployments.get_deployments_pipelines_by_pipeline_id_status(PIPELINE_ID)
    if status["is_promoted"] and status["current_deployment"]["status"] == "deployed":
        break
    time.sleep(10)

print("accuracy:", latest["accuracy_score"], "- serving")

6. Serve forecasts

Segment-based inference rebuilds the model's input from the segment's live data on every request — an empty body is a complete request. The pipeline URL survives re-trainings and re-deployments:

forecast = client.inference.post_inference_pipelines_by_pipeline_id_infer_segment(
    PIPELINE_ID, json={},
)

print(forecast["predictions"][0]["label"], "->",
      [round(v, 1) for v in forecast["predictions"][0]["point"]])
print("built from:", forecast["data_period"])
daily_sales -> [13102.4, 13350.9, 13571.2, ...]
built from: {'start': '2026-06-30T00:00:00Z', 'end': '2026-07-16T00:00:00Z'}

The loop is closed

Keep pushing signals as they happen — the live segment keeps model input fresh, and a training_schedule or retrain trigger retrains on your cadence. With promotion_policy: auto, a new training only replaces the serving model when it beats it, so accuracy ratchets upward.

On this page