Vercel AI SDK
Un solo middleware marca per l’AI Act e la California ciò che genera l’AI SDK: ogni immagine di generateImage e, con un modello incapsulato, ogni clip audio di generateSpeech e ogni video di experimental_generateVideo. Funziona con qualsiasi modello raggiungibile dall’SDK, tramite l’AI Gateway o il pacchetto di un provider.
In breve
- Pacchetto:
npm install @kennmarq/ai-sdk, con la chiave inKENNMARQ_API_KEYsul server. - Passaggio: incapsula il modello:
generateImagerestituirà le immagini marcate, e genererà un errore se una non si può marcare. - Modelli: qualsiasi, tra cui FLUX, GPT Image, Imagen e Recraft; le credenziali già aggiunte da un provider vengono conservate.
- Scritto per: AI SDK 7 (il pacchetto ai), verificato il 4 novembre 2026.
Marca ogni immagine: il 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 } });
}Le immagini create a partire da immagini di input vengono marcate come edited, le altre come created. La marcatura di ogni immagine è in providerMetadata.kennmarq.images, per la sua ricevuta. Per marcare ogni immagine creata da un provider, passa il middleware a wrapProvider, nel suo imageModelMiddleware.
Voce e video
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 voce riceve AudioSeal nel suono, che nessuno sente; i video (MP4) ricevono Video Seal in ogni fotogramma. Un video viene marcato come job, quindi la chiamata lo attende, e l’attesa rientra nel limite di tempo di experimental_generateVideo.
Oppure genera, poi marca
// 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 } });
}Le stesse due righe funzionano in una server action, o ovunque il tuo server chiami generateImage. Il modello qui viene dall’AI Gateway; con il pacchetto di un provider, passa invece il suo modello.
Più immagini insieme
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"));
}Immagini che contengono già credenziali
Alcuni provider firmano le loro immagini con proprie Content Credentials. Le conserviamo come ingrediente delle nostre, come chiede il codice di buone pratiche dell’UE, e aggiungiamo la tua azienda e la tua app, la filigrana e i cinque elementi della California.
Prossimi passi
- Preferisci non mantenere un helper? Il
mark()dell’SDK JavaScript accettaimage.uint8Arraycosì com’è. - Video e audio passano dallo stesso helper: vedi video.
- Cosa contiene ogni marcatura: Content Credentials e filigrane invisibili.
- Lascia che lo faccia il tuo agente di programmazione: il prompt.