Aller au contenu

Vercel AI SDK

Un seul middleware marque ce que génère l’AI SDK, pour l’AI Act et la Californie : chaque image de generateImage et, avec un modèle enveloppé, chaque fichier audio de generateSpeech et chaque vidéo de experimental_generateVideo. Il fonctionne avec tout modèle que le SDK atteint, via l’AI Gateway ou le package d’un fournisseur.

En bref

  • Package : npm install @kennmarq/ai-sdk, avec la clé dans KENNMARQ_API_KEY sur le serveur.
  • Étape : envelopper le modèle : generateImage renvoie alors les images marquées, et échoue si l’une d’elles ne peut pas être marquée.
  • Modèles : tous, dont FLUX, GPT Image, Imagen et Recraft ; les Content Credentials déjà ajoutés par un fournisseur sont conservés.
  • Écrit pour : AI SDK 7 (le package ai), vérifié le 4 novembre 2026.

Marquer chaque image : le middleware

app/api/image/route.ts
// app/api/image/route.ts
import { generateImage, wrapImageModel } from "ai";
import { openai } from "@ai-sdk/openai";
import { kennmarqImageMiddleware } from "@kennmarq/ai-sdk";

const model = wrapImageModel({
  model: openai.image("gpt-image-1"), // any image model, any provider
  middleware: kennmarqImageMiddleware({
    provider: "Acme Inc.",    // your legal name
    appName: "Acme Studio",   // your AI system, with its version
    appVersion: "2.1",
    markets: ["eu", "us-ca"],
  }),
});

export async function POST(request: Request) {
  const { prompt } = await request.json();
  const { image } = await generateImage({ model, prompt }); // already marked
  return new Response(image.uint8Array, { headers: { "Content-Type": image.mediaType } });
}

Les images créées à partir d’images d’entrée sont marquées comme edited, les autres comme created. Le marquage de chaque image se trouve dans providerMetadata.kennmarq.images, pour son justificatif. Pour marquer chaque image que crée un fournisseur, passez le middleware à wrapProvider, dans son option imageModelMiddleware.

Voix et vidéo

TypeScript
import { experimental_generateVideo, generateSpeech } from "ai";
import { fal } from "@ai-sdk/fal";
import { openai } from "@ai-sdk/openai";
import { kennmarqSpeechModel, kennmarqVideoModel } from "@kennmarq/ai-sdk";

const origin = { provider: "Acme Inc.", appName: "Acme Studio", appVersion: "2.1", markets: ["eu"] };

const { audio } = await generateSpeech({
  model: kennmarqSpeechModel(openai.speech("gpt-4o-mini-tts"), origin),
  text: "Hello!",
  outputFormat: "mp3", // MP3, WAV or M4A
});

const { video } = await experimental_generateVideo({
  model: kennmarqVideoModel(fal.video("luma-ray-2"), origin),
  prompt: "Waves at dusk",
});

La voix reçoit dans le son un marquage AudioSeal inaudible ; les vidéos (MP4) reçoivent Video Seal dans chaque image. Une vidéo est marquée sous forme de tâche : l’appel l’attend donc, et le délai maximal de experimental_generateVideo inclut cette attente.

Ou générer, puis marquer

app/api/image/route.ts
// app/api/image/route.ts
import { generateImage } from "ai";
import { mark } from "@/lib/kennmarq"; // the helper from the Next.js guide

export const maxDuration = 60;

export async function POST(request: Request) {
  const { prompt } = await request.json();
  const { image } = await generateImage({ model: "bfl/flux-3-image", prompt });

  const file = new Blob([image.uint8Array], { type: image.mediaType });
  const marked = await mark(file, `image.${image.mediaType.split("/")[1]}`, { model: "FLUX.3" });

  // Only the marked copy leaves this route. If mark() threw, the request fails instead.
  return new Response(marked, { headers: { "Content-Type": image.mediaType } });
}

Les deux mêmes lignes fonctionnent dans une server action, ou partout où votre serveur appelle generateImage. Le modèle vient ici de l’AI Gateway ; avec le package d’un fournisseur, passez plutôt son modèle.

Plusieurs images à la fois

TypeScript
const { images } = await generateImage({ model: "bfl/flux-3-image", prompt, n: 4 });

// One call each. One after another keeps a Free plan (10 calls a minute) well within its limit.
const marked: Blob[] = [];
for (const image of images) {
  marked.push(await mark(new Blob([image.uint8Array], { type: image.mediaType }), "image.png"));
}

Images qui portent déjà des Content Credentials

Certains fournisseurs signent leurs images avec leurs propres Content Credentials. Nous les conservons en tant qu’ingrédient des nôtres, comme le demande le code de bonne pratique de l’UE, et ajoutons par-dessus votre entreprise et votre application, le filigrane et les cinq éléments de la loi californienne.

Pour aller plus loin