← Back to home

Before / after — Vercel AI SDK

A real multi-variable system prompt: {{userName}}, {{planTier}}, {{locale}}. Same data flow, same model call. The only difference is when the typo gets caught.

Before Vanilla string template
import { generateText } from 'ai';
import { openai } from '@ai-sdk/openai';

const userName = 'alice';
const planTier = 'pro';
const locale = 'en-US';

// Typo intended to be {{userName}} — silently ships to the model.
const system = 'You are a support agent for {{usrName}} on plan {{planTier}}, locale {{locale}}.'
  .replace('{{userName}}', userName)
  .replace('{{planTier}}', planTier)
  .replace('{{locale}}', locale);

const { text } = await generateText({
  model: openai('gpt-4o-mini'),
  system,
  prompt: 'How do I upgrade my plan?'
});

// system is:
// "You are a support agent for {{usrName}} on plan pro, locale en-US."
//                                  ^^^^^^^ leaks to the model

The replace for {{userName}} doesn't match anything (the template has {{usrName}}), so the literal {{usrName}} reaches the model. The runtime works; the prompt is silently broken.

After tprompt
import { generateText } from 'ai';
import { openai } from '@ai-sdk/openai';
import { prompt } from '@nkwib/tprompt';

const supportSystem = prompt(
  'You are a support agent for {{usrName}} on plan {{planTier}}, locale {{locale}}.'
);

const { text } = await generateText({
  model: openai('gpt-4o-mini'),
  system: supportSystem.with({
    userName: 'alice',
    //  ^^^^^^^^ TS error: Property 'usrName' is missing in type
    //           '{ readonly userName: string; readonly planTier: string;
    //             readonly locale: string; }', but the call expects
    //           '{ readonly usrName: string; readonly planTier: string;
    //             readonly locale: string; }'.
    planTier: 'pro',
    locale: 'en-US'
  }),
  prompt: 'How do I upgrade my plan?'
});

tsc rejects this file before node ever runs. Either fix the template ({{userName}}) or fix the call site (usrName: 'alice') — the typo cannot land in production.

The wedge, in three lines of diff

-const system = 'You are a support agent for {{usrName}}...'
-  .replace('{{userName}}', userName)
+const supportSystem = prompt('You are a support agent for {{usrName}}...');
+supportSystem.with({ userName: 'alice', planTier, locale });
+//                   ~~~~~~~~ tsc: Property 'usrName' is missing

Try it

The Playground has the after-snippet pre-loaded. Rename {{usrName}} back to {{userName}} in the template literal — the error clears.

Open in TS Playground
tprompt Type-safe prompt templates for TypeScript