September 6, 2025

cNode SDK (Preview): Forecasts & Szenarien direkt im Code mit Mock-Demo zum Ausprobieren

von

Leonardo Bornhäußer

shallow focus photo of brown cardboard box
shallow focus photo of brown cardboard box
shallow focus photo of brown cardboard box

Warum ein SDK – und warum jetzt?

APIs sind mächtig, doch im Alltag zählt Developer-Speed. Das cNode SDK reduziert Boilerplate auf einen Funktionsaufruf: Cluster, Goal, Horizon, Inputs – fertig.
In der Preview-Phase können Sie bereits realistisch testen: Entweder komplett offline mit synthetischen Daten oder gegen einen Mock-Endpoint, bis die produktive SDK-Version als @cnode/sdk veröffentlicht wird.

Denkmodell: Cluster × Goals

Wie die API ist auch das SDK domänen- und zielbasiert:

  • Cluster (Preview):

    finance, product, hr, marketing, compliance, supply_chain
  • Typische Goals:

    • Finance:

      revenue_forecast, cashflow_forecast, break_even, profitability, roi_forecast, valuation_dcf, price_elasticity, anomaly_detection
    • Product:

      demand_forecast, inventory_coverage, return_rate_forecast
    • HR:

      headcount_forecast, attrition_forecast
    • Marketing:

      mql_forecast, cac_ltv_projection
    • Compliance:

      esg_risk_score, anomaly_detection

So binden Sie gezielt KPIs und Kontexte ein – ohne Blackbox.

Ergebnisformat: Forecasts, Intervalle, Explainability, Diagnostics

Der SDK-Call liefert strukturierte Ergebnisse:

  • Forecast: Zeitreihe (Datum/Wert)

  • Intervals: Konfidenzbänder (Lower/Upper + Confidence)

  • Explanations: wichtigste Treiber (global & optional lokal)

  • Diagnostics: Metriken (z. B. WAPE/MAE/RMSE), Warnungen, Retrain-Hinweise

Damit bleiben Prognosen prüfbar und erklärbar – ideal für Finance, ESG, Planning & Compliance.

Vollständiges SDK-Preview-Snippet (copy-paste)

Hinweis: Dieses Snippet enthält einen Mock-SDK-Shim, damit Sie heute bereits auf der Doku/Website eine Demo zeigen können. Ersetzen Sie ihn später durch @cnode/sdk.

/**
 * CNode SDK — Preview / Coming Soon (copy–paste)
 * -----------------------------------------------------------------------------
 * STATUS: The official SDK package is under active development.
 *         This snippet shows the intended params & responses and includes a
 *         small mock SDK so you can demo today on your docs/homepage.
 *
 * WHEN LIVE, REPLACE with:
 *   import { CNode } from "@cnode/sdk";
 *   const cnode = new CNode({ apiKey: process.env.CNODE_API_KEY });
 *
 * CLUSTERS (preview):
 *   "finance", "product", "hr", "marketing", "compliance", "supply_chain"
 *
 * COMMON GOALS by cluster (preview examples):
 *   finance:      "revenue_forecast", "cashflow_forecast", "break_even",
 *                 "profitability", "roi_forecast", "valuation_dcf",
 *                 "price_elasticity", "anomaly_detection"
 *   product:      "demand_forecast", "inventory_coverage", "return_rate_forecast"
 *   hr:           "headcount_forecast", "attrition_forecast"
 *   marketing:    "mql_forecast", "cac_ltv_projection"
 *   compliance:   "esg_risk_score", "anomaly_detection"
 *
 * RUN PARAMS (preview):
 *   {
 *     projectId: string,                         // required
 *     cluster: string,                           // required (see CLUSTERS)
 *     goal: string,                              // required (see GOALS)
 *     horizon: "3M"|"6M"|"12M"|string,           // required
 *     inputs: object,                            // required, domain-specific
 *     explainability?: boolean,                  // default: true
 *     options?: {
 *       confidence?: number,                     // 0.5..0.99; default: 0.8
 *       includeIntervals?: boolean,              // return prediction bands
 *       sensitivity?: { topK?: number },         // top drivers count
 *       backtest?: { from: string, to: string, metric?: "WAPE"|"MAE"|"RMSE" },
 *       retrain?: "auto"|"never",
 *       tags?: string[],
 *       metadata?: Record<string, unknown>
 *     }
 *   }
 *
 * RUN RESULT (preview):
 *   {
 *     run_id: string,
 *     forecast: Array<{ date: "YYYY-MM-DD", value: number }>,
 *     intervals?: Array<{ date: string, lower: number, upper: number, confidence: number }>,
 *     explanations?: {
 *       global_top_features: Array<{ feature: string, weight: number }>,
 *       local?: Array<{ date: string, contributions: Array<{ feature: string, weight: number }> }>
 *     },
 *     diagnostics?: {
 *       metrics?: { WAPE?: number, MAE?: number, RMSE?: number },
 *       retrain?: boolean,
 *       warnings?: string[]
 *     }
 *   }
 *
 * SECURITY NOTE:
 *   Keep real API keys server-side. For homepage demos, use mocked data or a
 *   short-lived preAuth token from a server route.
 */

// ---------- Types (preview) ----------
type Cluster =
  | "finance"
  | "product"
  | "hr"
  | "marketing"
  | "compliance"
  | "supply_chain"
  | (string & {}); // allow custom

type Goal =
  | "revenue_forecast"
  | "cashflow_forecast"
  | "break_even"
  | "profitability"
  | "roi_forecast"
  | "valuation_dcf"
  | "price_elasticity"
  | "anomaly_detection"
  | "demand_forecast"
  | "inventory_coverage"
  | "return_rate_forecast"
  | "headcount_forecast"
  | "attrition_forecast"
  | "mql_forecast"
  | "cac_ltv_projection"
  | "esg_risk_score"
  | (string & {}); // allow custom

type BacktestMetric = "WAPE" | "MAE" | "RMSE";

interface RunOptions {
  confidence?: number; // default: 0.8
  includeIntervals?: boolean;
  sensitivity?: { topK?: number }; // default: 5
  backtest?: { from: string; to: string; metric?: BacktestMetric };
  retrain?: "auto" | "never";
  tags?: string[];
  metadata?: Record<string, unknown>;
}

interface RunParams {
  projectId: string;
  cluster: Cluster;
  goal: Goal;
  horizon: "3M" | "6M" | "12M" | (string & {});
  inputs: Record<string, unknown>;
  explainability?: boolean;
  options?: RunOptions;
}

interface RunResult {
  run_id: string;
  forecast: Array<{ date: string; value: number }>;
  intervals?: Array<{ date: string; lower: number; upper: number; confidence: number }>;
  explanations?: {
    global_top_features: Array<{ feature: string; weight: number }>;
    local?: Array<{ date: string; contributions: Array<{ feature: string; weight: number }> }>;
  };
  diagnostics?: {
    metrics?: Partial<Record<BacktestMetric, number>>;
    retrain?: boolean;
    warnings?: string[];
  };
}

// ---------- Temporary SDK shim for demos (remove once SDK is live) ----------
class MockCNode {
  constructor(
    private cfg: {
      /**
       * If you supply a baseUrl (e.g. "/api/mock/cnode"), we'll POST to:
       *   `${baseUrl}/scenarios:run`
       * Otherwise we synthesize a deterministic forecast locally.
       */
      baseUrl?: string;
      /**
       * Do NOT put real API keys in client-side code. This is a placeholder to
       * illustrate the SDK signature only.
       */
      apiKey?: string;
      apiVersion?: string; // e.g. "2025-09-01"
    } = {}
  ) {
    this.cfg.apiVersion ??= "2025-09-01";
  }

  scenarios = {
    run: async (p: RunParams): Promise<RunResult> => {
      if (this.cfg.baseUrl) {
        // Proxy to your mock endpoint while backend is under development
        const res = await fetch(`${this.cfg.baseUrl}/scenarios:run`, {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
            "X-API-Version": this.cfg.apiVersion!,
            "X-Demo-Mode": "true",
            // Example only; never ship real keys in browser
            Authorization: this.cfg.apiKey ? `Bearer ${this.cfg.apiKey}` : undefined
          } as Record<string, string>,
          body: JSON.stringify({
            project_id: p.projectId,
            cluster: p.cluster,
            goal: p.goal,
            horizon: p.horizon,
            inputs: p.inputs,
            explainability: p.explainability ?? true,
            options: p.options,
            _note: "SDK-preview: replace mock route with production once live"
          })
        });

        if (!res.ok) {
          const err = await res.json().catch(() => ({}));
          throw new Error(`Mock SDK ${res.status}: ${err.message ?? res.statusText}`);
        }
        return res.json();
      }

      // Fallback: synthesize a deterministic, upward-trending demo series
      const months = parseHorizonToMonths(p.horizon);
      const start = new Date(Date.UTC(2025, 0, 1));
      const base = 42000;
      const step = 3500;

      const forecast = Array.from({ length: months }, (_, i) => {
        const d = new Date(start);
        d.setUTCMonth(d.getUTCMonth() + i);
        const trend = base + step * i;
        const season = 1 + 0.1 * Math.sin((2 * Math.PI * i) / 12); // ±10% seasonality
        return { date: d.toISOString().slice(0, 10), value: Math.round(trend * season) };
      });

      const intervals = forecast.map((pnt) => ({
        date: pnt.date,
        lower: Math.round(pnt.value * 0.9),
        upper: Math.round(pnt.value * 1.1),
        confidence: (p.options?.confidence ?? 0.8)
      }));

      const explanations: RunResult["explanations"] = {
        global_top_features: [
          { feature: "trend", weight: 0.45 },
          { feature: "seasonality", weight: 0.32 },
          { feature: "promo_index", weight: 0.11 }
        ]
      };

      const diagnostics: RunResult["diagnostics"] = {
        metrics: p.options?.backtest ? { WAPE: 0.14, MAE: 5200, RMSE: 7300 } : undefined,
        retrain: p.options?.retrain === "auto",
        warnings: []
      };

      return {
        run_id: "mock-sdk-run-001",
        forecast,
        intervals: p.options?.includeIntervals !== false ? intervals : undefined,
        explanations: p.explainability === false ? undefined : explanations,
        diagnostics
      };
    }
  };
}

// ---------- Helper ----------
function parseHorizonToMonths(h: RunParams["horizon"]): number {
  if (typeof h !== "string") return 12;
  const m = h.match(/^(\d+)\s*(M|m|months?)$/);
  if (m) return Math.max(1, parseInt(m[1], 10));
  const y = h.match(/^(\d+)\s*(Y|y|years?)$/);
  if (y) return Math.max(1, parseInt(y[1], 10) * 12);
  // common presets
  if (h === "3M") return 3;
  if (h === "6M") return 6;
  if (h === "12M") return 12;
  return 12;
}

// ---------- Example usage for docs/homepage ----------
(async () => {
  // OPTION A (no network): synth data only
  const cnode = new MockCNode();

  // OPTION B (with mock API): POST to your server route until prod is live
  // const cnode = new MockCNode({ baseUrl: "/api/mock/cnode" });

  const run = await cnode.scenarios.run({
    projectId: "demo-finance",
    cluster: "finance",
    goal: "revenue_forecast", // try: "cashflow_forecast", "valuation_dcf"
    horizon: "12M",
    inputs: {
      currency: "EUR",
      revenue_history: [
        { date: "2024-09-01", value: 42000 },
        { date: "2025-08-01", value: 86500 }
      ],
      cost_history: [
        { date: "2024-09-01", value: -28000 },
        { date: "2025-08-01", value: -52500 }
      ],
      promo_index: [{ date: "2025-06-01", value: 0.6 }]
    },
    explainability: true,
    options: {
      confidence: 0.8,
      includeIntervals: true,
      sensitivity: { topK: 5 },
      backtest: { from: "2025-01-01", to: "2025-08-01", metric: "WAPE" },
      retrain: "auto",
      tags: ["homepage-demo"],
      metadata: { source: "marketing-site" }
    }
  });

  console.log("Run:", run.run_id);
  console.table(run.forecast.slice(0, 6)); // preview first 6 points
  if (run.intervals) console.table(run.intervals.slice(0, 3));
  console.log("Top drivers:", run.explanations?.global_top_features?.slice(0, 5));
  console.log("Metrics:", run.diagnostics?.metrics);
})();

Sicherheit & Governance

  • API-Keys nie im Browser – serverseitig oder per kurzlebigem preAuth-Token.

  • EU-Hosting, DSGVO-Konformität, Mandanten-Isolation.

  • Versionierung & Audit-Trails via Governance-Layer.

Roadmap & GA-Hinweis

Die SDK-Signaturen stehen – Details können sich bis GA noch ändern. Ziel: ein stabil versioniertes npm-Package (@cnode/sdk) mit klarer SemVer-Strategie und Developer-Guides.

Weitere Beiträge entdecken

Bleib auf dem Laufenden mit unseren neuesten Artikeln zu datenbasierter Planung, KI-gestützter Szenariosteuerung und Best Practices aus der Anwendung von cNode.