CHARM c3-charm

Most time-series models are built for one job on one dataset: a forecaster can't flag anomalies, and a model trained on one fleet of machines rarely transfers to the next. Teams end up building and maintaining a separate model for every task and every sensor layout.

CHARM replaces that sprawl with one pretrained model. It reads a window of sensor channels together with a short text description of each — for example "oil_pressure" or "ambient_temp" — and turns them into general-purpose representations that drive forecasting, anomaly detection, retrieval and classification, with no per-task training. Because it learns from the shape of signals rather than memorizing raw values, it stays robust to noise and transfers across domains and sensor configurations.

Your dataseries + channel
descriptions
→
CharmClientvalidates,
batches
→
CHARMserved model
(frozen)
→
Resultsembeddings ·
forecasts
→
Your codeforecast · detect
· search

You send raw values and channel names; CHARM normalizes internally and returns predictions in your original units. No training, no per-task models.

How it works

CHARM runs server-side; the c3-charm SDK is the client. It offers two capabilities:

CapabilityWhat it gives you
EmbeddingsOne dense vector per series — for similarity search, clustering, anomaly detection, or features for classical ML
Forecast / backcastZero-shot quantile predictions with uncertainty, no training data required

Key ideas

  • Zero-shot. Forecasting needs no training data; one call returns a full predictive distribution. Embeddings are frozen features you can use immediately.
  • Channel-aware. Channel descriptions are model input, not metadata — meaningful names improve results. Send the related channels of one system together.
  • Self-normalizing. Send raw values; the model normalizes each window internally and returns forecasts in your original scale.
  • Patch-based. Time is split into patches of 16 steps, so a window needs a few patches to carry signal — roughly 48 steps is a sensible floor.
  • Discover, don't assume. Read the embedding dimension and quantile count from the model's own metadata and the output shapes rather than hardcoding them.

Next steps

Quick start

Install the SDK, point it at a CHARM endpoint, and run your first forecast and embeddings in a few minutes. Everything below uses the real c3-charm API.

1. Install

Install the SDK with pip (Python ≥ 3.10). On Linux, install the small CPU build of PyTorch first so you don't pull the ~4 GB CUDA wheel — the model runs server-side, so you never need a GPU.

pip install torch --index-url https://download.pytorch.org/whl/cpu   # optional, Linux: smaller CPU torch
pip install c3-charm

2. Point the client at an endpoint

CHARM is served over an API. Set your endpoint and key as environment variables; the client reads them automatically. (No endpoint yet? See getting access.)

export CHARM_BASE_URL=https://your-charm-server:8080
export CHARM_API_KEY=your-api-key
from charm import CharmClient

client = CharmClient(timeout=300)   # reads CHARM_BASE_URL / CHARM_API_KEY
print(client.model_info())          # checks reachability and shows the served model's properties

3. Prepare your data

Send raw values as plain Python lists, time-major (N, T, C), with a meaningful text description per channel. CHARM normalizes internally, so don't pre-scale.

import numpy as np

# Two channels of one system, 512 hourly steps, raw units, no NaNs
t = np.arange(512)
load = 100 + 20 * np.sin(2 * np.pi * t / 24) + np.random.randn(512)
temp = 15 + 5 * np.sin(2 * np.pi * (t - 3) / 24) + 0.5 * np.random.randn(512)
series = np.stack([load, temp], axis=1)          # (T=512, C=2), time-major

descriptions = [["load_mw", "ambient_temp_c"]]   # (N=1, C=2) channel names
ts_array = [series.tolist()]                     # (N=1, T, C) as nested lists

4. Forecast with uncertainty

One call returns a full quantile distribution — no training. Use target_len for the horizon (negative values reconstruct the recent past instead). median is the point forecast; denormalized_predictions carries the quantiles for intervals.

fc = client.prediction.create(
    descriptions=descriptions, ts_array=ts_array,
    target_len=48, return_tensors="np", progress=False,
)
point = fc.median                       # (1, 48, 2) — point forecast
quantiles = fc.denormalized_predictions # (1, 48, 2, Q) — full predictive distribution

5. Turn history into embeddings

Embed sliding windows to get one vector per window — the basis for similarity search, clustering, and anomaly detection.

windows = [series[i:i + 128].tolist() for i in range(0, 512 - 128 + 1, 32)]
emb = client.embeddings.create(
    descriptions=descriptions * len(windows), ts_array=windows,
    return_tensors="np", progress=False,
).embeds                                # (13, D) — one vector per window

Next

Example use cases maps common goals to the right calls; CHARM with coding agents installs the skill so your agent writes this for you. Full API detail (shapes, batching, quantile levels, errors) ships in the package docs — run python -c "import charm.docs as d; print(d.get_docs_path())".

When to use CHARM

SituationRecommendation
Multivariate or univariate series, ≥ ~48 steps, and you want results without feature engineeringGood fit
You need a forecast with uncertainty and have no training dataGood fit — zero-shot forecasting
You want to find similar periods, cluster assets, or flag unusual behavior without labelsGood fit — embeddings + kNN
Tabular data with no time axisUse gradient boosting instead
Very short series (fewer than ~48 steps)Prefer classical methods

What you can build

CHARM's two capabilities — embeddings and zero-shot forecasts — cover a wide range of time-series work. Scan the groups below and adapt the closest one to your data.

Forecasting

  • Predict the next hours or days of demand, load, or sensor values with calibrated confidence bands.
  • Produce prediction intervals for capacity planning and risk-aware decisions, not just a point estimate.
  • Fill gaps in history by forecasting from the data before each gap.
  • Backtest against a seasonal-naive baseline before trusting the band edges.

Anomaly detection

  • Flag unusual behavior in machine or process telemetry with no labeled failures.
  • Score each window by its distance to normal behavior; bootstrap a clean reference when you have none.
  • Catch level shifts and spikes by combining embeddings with simple window statistics.
  • Keep per-channel resolution on high-dimensional assets so an anomaly in a few channels isn't averaged away.

Retrieval and similarity

  • Answer "when did this happen before?" by searching embedded windows of history.
  • Find assets across a fleet that behave like a given one.
  • Cluster operating regimes or group similar time periods.

Features and trained heads

  • Use embeddings as drop-in features for sklearn / gradient-boosting — the fastest supervised baseline.
  • Classify series (fault type, activity, regime) from embeddings when you have labels.
  • Train a small forecasting, reconstruction, or classification head on frozen embeddings when a zero-shot baseline isn't enough.

Choose your approach

Describe what the application must decide or show, then work back to the CHARM calls.

GoalApproach
Forecast the next H steps with honest uncertaintyA single zero-shot forecast; surface the quantile intervals, not just the median
Fill a gap in historyForecast from the history before the gap (a backcast reconstructs seen data, it is not an imputer)
Find similar periods or assetsEmbed sliding windows once, then cosine kNN
Detect anomalies without labelsScore windows by distance to normal; bootstrap a clean reference; add window statistics
Features for classical MLFeed embeddings (+ your metadata) into sklearn — the fastest supervised baseline
Best accuracy on your own dataTrain a small toolkit head on precomputed embeddings

Start with the zero-shot or embeddings-plus-classical option; move to a trained head only if the baseline isn't good enough on held-out data.

CHARM with coding agents

The CHARM agent skill teaches Claude Code, Cursor, Codex and others when and how to apply CHARM. It keeps API details out of the skill and routes the agent to the SDK's shipped docs, so guidance matches the installed version.

Install

claude plugin marketplace add https://huggingface.co/c3aiia3c/CHARM.git
claude plugin install charm@c3-charm

Cursor auto-discovers skills from a skills directory (it doesn't use claude plugin). Copy the skill in, then start a new Cursor session and invoke it with /charm in Agent chat.

git clone https://huggingface.co/c3aiia3c/CHARM
cp -r CHARM/skills/charm ~/.cursor/skills/charm   # all projects
cp -r CHARM/skills/charm .cursor/skills/charm     # this project only (commit it to share with your team)

Paste this into your coding agent:

Install the CHARM skill. If you're in Claude Code, run
`claude plugin marketplace add https://huggingface.co/c3aiia3c/CHARM.git`, then
`claude plugin install charm@c3-charm`. Otherwise, read the skill at
https://huggingface.co/c3aiia3c/CHARM/resolve/main/skills/charm/SKILL.md and copy
skills/charm/ into your agent's skills directory. Use one method.
git clone https://huggingface.co/c3aiia3c/CHARM
cp -r CHARM/skills/charm ~/.claude/skills/charm       # all projects
cp -r CHARM/skills/charm .claude/skills/charm         # this project only

Getting access

CHARM is served behind an API. Set CHARM_BASE_URL and CHARM_API_KEY to point the SDK at an endpoint. To obtain access, contact gerardo.pastrana@c3.ai and dhruv.mehta@c3.ai. The model will also be available on Azure AI Foundry — instructions to follow.

Example prompts

  • "Forecast the next 48 hours of this load data with confidence bands, without training a model."
  • "Our logger dropped out for six hours last Tuesday. Estimate the missing readings."
  • "Flag unusual behavior in this pump telemetry. We have no labeled failures."
  • "Find the most similar periods to this vibration pattern across the fleet's history."