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é dansKENNMARQ_API_KEYsur le serveur. - Étape : envelopper le modèle :
generateImagerenvoie 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
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
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
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
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
- Vous préférez ne pas maintenir de fonction utilitaire ? Le
mark()du SDK JavaScript accepteimage.uint8Arraytel quel. - Les vidéos et l’audio passent par la même fonction utilitaire : voir Vidéos.
- Ce que porte chaque marquage : Content Credentials et filigranes invisibles.
- Laissez votre agent de codage s’en charger : le prompt.