0d3cb498a3
CI / Shell Format Check (push) Has been cancelled
CI / Check Ruby (3.4) (push) Has been cancelled
CI / CI Config (push) Has been cancelled
CI / Test on Node ${{ matrix.node }} and ${{ matrix.os }}${{ matrix.shard && format(' (shard {0}/3)', matrix.shard) || '' }} (push) Has been cancelled
CI / Build on Node ${{ matrix.node }} (push) Has been cancelled
CI / Style Check (push) Has been cancelled
CI / Generate Assets (push) Has been cancelled
CI / Check Python (3.14) (push) Has been cancelled
CI / Check Python (3.9) (push) Has been cancelled
CI / Build Docs (push) Has been cancelled
CI / Code Scan Action (push) Has been cancelled
CI / Site tests (push) Has been cancelled
CI / webui tests (push) Has been cancelled
CI / Run Integration Tests (push) Has been cancelled
CI / Run Smoke Tests (push) Has been cancelled
CI / Go Tests (push) Has been cancelled
CI / Share Test (push) Has been cancelled
CI / Redteam (Production API) (push) Has been cancelled
CI / Redteam (Staging API) (push) Has been cancelled
CI / GitHub Actions Lint (push) Has been cancelled
CI / Check Ruby (3.0) (push) Has been cancelled
release-please / release-please (push) Has been cancelled
release-please / build (push) Has been cancelled
release-please / publish-npm (push) Has been cancelled
release-please / publish-npm-backfill (push) Has been cancelled
release-please / docker (push) Has been cancelled
release-please / publish-code-scan-action (push) Has been cancelled
release-please / attest-code-scan-action (push) Has been cancelled
Deploy local.promptfoo.app / Deploy to Cloudflare Pages (push) Has been cancelled
Test and Publish Multi-arch Docker Image / test (push) Has been cancelled
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-amd64 platform:linux/amd64 runner:ubuntu-latest]) (push) Has been cancelled
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-arm64 platform:linux/arm64 runner:ubuntu-24.04-arm]) (push) Has been cancelled
Test and Publish Multi-arch Docker Image / merge-docker-digests (push) Has been cancelled
Test and Publish Multi-arch Docker Image / Attest Multi-arch Image (push) Has been cancelled
Validate Renovate Config / Validate Renovate Configuration (push) Has been cancelled
193 lines
5.5 KiB
JavaScript
193 lines
5.5 KiB
JavaScript
/**
|
|
* Vercel AI SDK Provider with Dynamic Prompt Reporting
|
|
*
|
|
* This provider demonstrates a real-world pattern: dynamically constructing
|
|
* prompts based on context, then reporting the actual prompt back to promptfoo.
|
|
*
|
|
* Why this matters:
|
|
* - Without prompt reporting, promptfoo shows "{{topic}}" as the prompt
|
|
* - With prompt reporting, you see the full system instructions and context
|
|
* - This enables prompt-based assertions and debugging
|
|
*
|
|
* The Vercel AI SDK (https://ai-sdk.dev) is the TypeScript toolkit for AI apps
|
|
* with 20M+ monthly downloads, supporting OpenAI, Anthropic, Google, and more.
|
|
*
|
|
* Usage:
|
|
* npm install ai @ai-sdk/openai
|
|
* OPENAI_API_KEY=sk-... npx promptfoo@latest eval
|
|
*/
|
|
import { openai } from '@ai-sdk/openai';
|
|
import { generateText } from 'ai';
|
|
|
|
// =============================================================================
|
|
// PROMPT TEMPLATES - These simulate what frameworks like LangChain do
|
|
// =============================================================================
|
|
|
|
const SYSTEM_TEMPLATES = {
|
|
expert: `You are a world-class expert in {{domain}}.
|
|
|
|
Your communication style:
|
|
- Clear and precise explanations
|
|
- Use analogies for complex concepts
|
|
- Include concrete examples
|
|
- Acknowledge limitations honestly
|
|
|
|
Your audience: {{audience}}`,
|
|
|
|
coder: `You are an expert software engineer specializing in {{domain}}.
|
|
|
|
Guidelines:
|
|
- Write clean, idiomatic code
|
|
- Explain your reasoning
|
|
- Consider edge cases
|
|
- Suggest best practices
|
|
|
|
Target experience level: {{audience}}`,
|
|
|
|
analyst: `You are a data analyst specializing in {{domain}}.
|
|
|
|
Your approach:
|
|
- Ground claims in evidence
|
|
- Quantify when possible
|
|
- Consider multiple perspectives
|
|
- Identify key insights
|
|
|
|
Report format: {{format}}`,
|
|
};
|
|
|
|
const USER_TEMPLATES = {
|
|
explain: `Explain {{topic}} in a way that's accessible and engaging.
|
|
|
|
Focus on:
|
|
1. Core concepts and why they matter
|
|
2. Real-world applications
|
|
3. Common misconceptions to avoid`,
|
|
|
|
compare: `Compare and contrast: {{topic}}
|
|
|
|
Structure your response as:
|
|
1. Key similarities
|
|
2. Key differences
|
|
3. When to use each
|
|
4. Recommendations`,
|
|
|
|
troubleshoot: `Help troubleshoot: {{topic}}
|
|
|
|
Provide:
|
|
1. Common causes
|
|
2. Diagnostic steps
|
|
3. Solutions ranked by likelihood
|
|
4. Prevention strategies`,
|
|
};
|
|
|
|
// =============================================================================
|
|
// DYNAMIC PROMPT BUILDER - The core of this example
|
|
// =============================================================================
|
|
|
|
function buildPrompt(rawPrompt, vars) {
|
|
// Determine the best template based on the task
|
|
const taskType = vars.task_type || 'explain';
|
|
const persona = vars.persona || 'expert';
|
|
|
|
// Get templates
|
|
const systemTemplate = SYSTEM_TEMPLATES[persona] || SYSTEM_TEMPLATES.expert;
|
|
const userTemplate = USER_TEMPLATES[taskType] || USER_TEMPLATES.explain;
|
|
|
|
// Fill in template variables
|
|
const fillTemplate = (template, variables) => {
|
|
return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
|
|
return variables[key] || match;
|
|
});
|
|
};
|
|
|
|
const templateVars = {
|
|
topic: rawPrompt,
|
|
domain: vars.domain || 'the requested topic',
|
|
audience: vars.audience || 'general audience',
|
|
format: vars.format || 'clear prose',
|
|
...vars,
|
|
};
|
|
|
|
const systemPrompt = fillTemplate(systemTemplate, templateVars);
|
|
const userPrompt = fillTemplate(userTemplate, templateVars);
|
|
|
|
// Add any retrieved context (simulating RAG)
|
|
let contextAddition = '';
|
|
if (vars.context) {
|
|
contextAddition = `\n\nRelevant context:\n${vars.context}`;
|
|
}
|
|
|
|
// Add few-shot examples if provided
|
|
let examplesAddition = '';
|
|
if (vars.examples) {
|
|
examplesAddition = `\n\nExamples for reference:\n${vars.examples}`;
|
|
}
|
|
|
|
return {
|
|
system: systemPrompt,
|
|
user: userPrompt + contextAddition + examplesAddition,
|
|
};
|
|
}
|
|
|
|
// =============================================================================
|
|
// PROVIDER CLASS
|
|
// =============================================================================
|
|
|
|
export default class AiSdkProvider {
|
|
constructor(options = {}) {
|
|
this.config = options.config || {};
|
|
this.modelId = this.config.model || 'gpt-4o-mini';
|
|
this.temperature = this.config.temperature ?? 0.7;
|
|
}
|
|
|
|
id() {
|
|
return `ai-sdk:${this.modelId}`;
|
|
}
|
|
|
|
async callApi(prompt, context) {
|
|
const vars = context.vars || {};
|
|
|
|
// Build the dynamic prompt
|
|
const { system, user } = buildPrompt(prompt, vars);
|
|
|
|
// Construct the messages array (what we'll actually send)
|
|
const messages = [
|
|
{ role: 'system', content: system },
|
|
{ role: 'user', content: user },
|
|
];
|
|
|
|
try {
|
|
// Call the LLM using Vercel AI SDK
|
|
const result = await generateText({
|
|
model: openai(this.modelId),
|
|
messages,
|
|
temperature: this.temperature,
|
|
maxOutputTokens: this.config.maxOutputTokens,
|
|
});
|
|
|
|
return {
|
|
output: result.text,
|
|
|
|
// THE KEY FEATURE: Report what prompt was actually sent
|
|
// This enables:
|
|
// 1. UI shows "Actual Prompt Sent" instead of the template
|
|
// 2. Assertions can check the real prompt content
|
|
// 3. Moderation runs on the actual prompt
|
|
prompt: messages,
|
|
|
|
tokenUsage: {
|
|
prompt: result.usage?.inputTokens,
|
|
completion: result.usage?.outputTokens,
|
|
total: result.usage?.totalTokens,
|
|
},
|
|
};
|
|
} catch (error) {
|
|
return {
|
|
error: `AI SDK error: ${error.message}`,
|
|
// Still report the prompt even on error - useful for debugging
|
|
prompt: messages,
|
|
};
|
|
}
|
|
}
|
|
}
|