> ## Documentation Index
> Fetch the complete documentation index at: https://ai.aidalinfo.fr/llms.txt
> Use this file to discover all available pages before exploring further.

# Assembler un workflow simple

> Installer le module Workflows, créer vos étapes et exécuter un pipeline complet.

Les workflows AI Kit s’appuient sur des étapes typées (`createStep`, `createMapStep`, etc.) enchaînées via `createWorkflow`. Chaque run est observable, annulable et compatible avec la télémétrie OpenTelemetry.

## 1. Installer les dépendances

```bash theme={null}
pnpm add @ai_kit/core zod
# ou
npm install @ai_kit/core zod
```

`zod` est facultatif mais recommandé pour typer vos entrées/sorties.

## 2. Déclarer une étape

```ts theme={null}
import { createStep } from "@ai_kit/core";
import { z } from "zod";

export const fetchWeather = createStep({
  id: "fetch-weather",
  description: "Récupère la météo courante",
  inputSchema: z.object({ city: z.string().min(1) }),
  outputSchema: z.object({ forecast: z.string() }),
  handler: async ({ input, signal }) => {
    if (signal.aborted) {
      throw new Error("Requête annulée");
    }

    // TODO: remplacez par un appel API réel
    return { forecast: `Il fait beau à ${input.city}` };
  },
});
```

* **Pas de generics nécessaires** : les types `Input` / `Output` sont inférés automatiquement dès que vous fournissez `inputSchema` / `outputSchema`.
* Ces steps restent compatibles avec n’importe quel workflow, même lorsqu’il déclare un `ctx` ou des métadonnées typées. Vous n’ajoutez des generics que si vous voulez du typage avancé (`Meta`, `RootInput`, `Ctx`) dans la step elle-même.
* Pas de schéma ? Vous pouvez toujours typer manuellement (`createStep<MonInput, MonOutput>(...)`) comme dans les versions précédentes.

## 3. Assembler un workflow

```ts theme={null}
import { createWorkflow } from "@ai_kit/core";
import { z } from "zod";
import { fetchWeather } from "./steps/fetchWeather";

export const weatherWorkflow = createWorkflow({
  id: "weather-line",
  description: "Workflow météo simple",
  inputSchema: z.object({ city: z.string() }),
  outputSchema: z.object({ forecast: z.string() }),
})
  .then(fetchWeather)
  .commit();
```

`commit()` retourne une instance immuable de `Workflow`. Le schéma de sortie est appliqué sur la valeur renvoyée par la dernière étape (ou `finalize` si vous l’avez défini).

## 4. Exécuter et inspecter

```ts theme={null}
const result = await weatherWorkflow.run({
  inputData: { city: "Paris" },
});

if (result.status === "success") {
  console.log(result.result.forecast);
} else {
  console.error("Échec", result.error);
}
```

### Contrôler l’exécution

* `workflow.createRun()` retourne un `WorkflowRun` réutilisable.
* `run.watch(listener)` reçoit un événement à chaque étape (`workflow:start`, `step:success`, `step:event`, etc.).
* `run.stream()` expose un itérateur asynchrone pour consommer les événements en direct tout en attendant la résolution.
* `run.cancel()` annule proprement l’exécution via un `AbortSignal`.

```ts theme={null}
const run = weatherWorkflow.createRun();

const unwatch = run.watch(event => {
  console.log(`[${event.type}]`, event);
});

const { stream, final } = await run.stream({ inputData: { city: "Lyon" } });

for await (const evt of stream) {
  // UI temps réel ou logs applicatifs
}

const outcome = await final;
unwatch();
```

### Métadonnées partagées

Initialisez un objet partagé via `metadata` lors du `run.start()` ou `run.stream()`. Accédez-y dans une étape avec `context.getMetadata()` et mettez-le à jour avec `context.updateMetadata()`. `context.store` expose une `Map` partagée pour stocker des références temporaires.

```ts theme={null}
const notifyTeam = createStep({
  id: "notify-team",
  handler: async ({ context }) => {
    context.emit({ type: "notification", data: { channel: "slack" } });
    return { status: "sent" };
  },
});
```

### Contexte d’exécution (`ctx`)

Transportez un contexte typé entre les étapes :

```ts theme={null}
type OrderCtx = {
  orgId: string;
  total: number;
  userId?: string;
};

const orderWorkflow = createWorkflow({
  id: "order-processing",
  ctx: { orgId: "default-org", total: 0 } as OrderCtx,
})
  .then(
    createStep({
      id: "apply-amount",
      handler: ({ input, ctx, stepRuntime }) => {
        stepRuntime.updateCtx(current => ({
          ...current,
          total: current.total + input.amount,
          userId: input.userId,
        }));
        return `Enregistré pour ${ctx.orgId}`;
      },
    }),
  )
  .then(
    createStep({
      id: "format-summary",
      handler: ({ ctx }) => `Organisation ${ctx.orgId} — total ${ctx.total}`,
    }),
  )
  .commit();

const run = await orderWorkflow.run({
  inputData: { amount: 120, userId: "user_42" },
  ctx: { orgId: "acme-co" },
});

console.log(run.ctx);
// { orgId: "acme-co", total: 120, userId: "user_42" }
```

### Choisir son niveau de typage

* **Workflow minimal** – Aucun generic, donc pas d’autocomplétion sur `ctx`/`metadata`, mais vous enchaînez vos steps très rapidement :

```ts theme={null}
const workflow = createWorkflow({ id: "simple" })
  .then(fetchWeather)
  .commit();
```

* **Workflow typé** – Ajoutez `<Input, Output, Meta, Ctx>` pour bénéficier d’un typage complet (`ctx.orgId`, `metadata.processed`, etc.). Les steps basées sur des schémas restent plug-and-play : aucun `<…>` supplémentaire lorsque vous les enchaînez.

## 5. Exemple complet (agent + workflow)

```ts theme={null}
import { Agent, createStep, createWorkflow, scaleway } from "@ai_kit/core";
import { z } from "zod";

const weatherSnapshotSchema = z.object({
  location: z.string(),
  temperature: z.number(),
  feelsLike: z.number(),
  humidity: z.number(),
  windSpeed: z.number(),
  windGust: z.number(),
  conditions: z.string(),
});

type WeatherSnapshot = z.infer<typeof weatherSnapshotSchema>;

const weatherCodeLabels: Record<number, string> = {
  0: "Ciel dégagé",
  1: "Principalement dégagé",
  2: "Nuages épars",
  3: "Couvert",
  45: "Brouillard",
  48: "Brouillard givrants",
  51: "Bruine légère",
  53: "Bruine modérée",
  55: "Bruine dense",
  56: "Bruine verglaçante légère",
  57: "Bruine verglaçante intense",
  61: "Pluie légère",
  63: "Pluie modérée",
  65: "Pluie forte",
  66: "Pluie verglaçante légère",
  67: "Pluie verglaçante forte",
  71: "Neige légère",
  73: "Neige modérée",
  75: "Neige forte",
  77: "Grain de neige",
  80: "Averses de pluie légères",
  81: "Averses de pluie modérées",
  82: "Averses de pluie violentes",
  85: "Averses de neige légères",
  86: "Averses de neige fortes",
  95: "Orage léger",
  96: "Orage avec grêle légère",
  99: "Orage avec grêle forte",
};

const fetchWeather = createStep({
  id: "fetch-weather",
  description: "Récupère la météo courante via un service externe",
  inputSchema: z.object({ city: z.string().min(1) }),
  outputSchema: z.object({ forecast: weatherSnapshotSchema }),
  handler: async ({ input, signal }) => {
    const response = await fetch(
      `https://api.open-meteo.com/v1/forecast?current=temperature_2m,apparent_temperature,relative_humidity_2m,wind_speed_10m,wind_gusts_10m,weather_code&timezone=Europe/Paris&hourly=temperature_2m&daily=weather_code&latitude=48.8566&longitude=2.3522`,
      { signal },
    );

    if (!response.ok) {
      throw new Error(`Erreur sur l'API météo (${response.status})`);
    }

    const body = await response.json();
    const current = body.current;

    const snapshot: WeatherSnapshot = {
      location: input.city,
      temperature: current.temperature_2m,
      feelsLike: current.apparent_temperature,
      humidity: current.relative_humidity_2m,
      windSpeed: current.wind_speed_10m,
      windGust: current.wind_gusts_10m,
      conditions:
        weatherCodeLabels[current.weather_code as keyof typeof weatherCodeLabels] ??
        "Conditions inconnues",
    };

    return { forecast: snapshot };
  },
});

const generateAdvice = createStep({
  id: "generate-advice",
  description: "Produit un message conseils grâce à un agent",
  inputSchema: z.object({ forecast: weatherSnapshotSchema }),
  outputSchema: z.object({ text: z.string() }),
  handler: async ({ input, context }) => {
    context.emit({
      type: "forecast",
      data: input.forecast,
    });

    const agent = new Agent({
      name: "weather-advisor",
      instructions:
        "Tu es un assistant météo qui donne des conseils pratiques et un ton chaleureux.",
      model: scaleway("gpt-oss-120b"),
    });

    const advice = await agent.generate({
      prompt: `Ville: ${input.forecast.location}
Température ressentie: ${input.forecast.feelsLike}°C
Conditions: ${input.forecast.conditions}
Humidité: ${input.forecast.humidity}%
Vent: ${input.forecast.windSpeed} km/h (rafales ${input.forecast.windGust} km/h)

Propose un court message en français qui résume la météo et donne un conseil concret.`,
    });

    return { text: advice.text };
  },
});

export const weatherAdvisorWorkflow = createWorkflow({
  id: "weather-advisor",
  description: "Workflow de conseils météo piloté par un agent",
})
  .then(fetchWeather)
  .then(generateAdvice)
  .commit();
```

Ce workflow combine une étape “automatique” (appel API) et une étape IA (génération via `Agent`). Les événements `context.emit` peuvent alimenter une interface temps réel lors du run.
