Migrating from Promptfoo
EvalGuard is compatible with Promptfoo config files. Most migrations take under 5 minutes.
Quick Start
1. Point eval:local at your existing config
npx @evalguard/cli eval:local promptfooconfig.yaml
EvalGuard reads Promptfoo YAML configs directly. Standard assert types are mapped to EvalGuard scorers automatically — e.g. llm-rubric → llm-grader, model-graded-factuality → factuality, is-json → json-valid, similar → semantic-similarity.
eval:local calls the model on your machine, so set a provider key first — e.g. export OPENAI_API_KEY=… (or whichever provider your config targets). To smoke-test the wiring without a key, add --provider echo, which echoes each prompt back so scorers run offline.
2. Or convert to an explicit evalguard.config.json
npx @evalguard/cli import:promptfoo promptfooconfig.yaml
Writes a fully-converted evalguard.config.json you can review and commit, and prints a summary of any assertion types that need manual attention. The converted output looks like:
// evalguard.config.json (produced by import:promptfoo)
{
"name": "Imported from Promptfoo",
"providers": [{ "provider": "openai", "model": "gpt-4o-mini" }],
"prompts": ["Answer: {{input}}"],
"cases": [
{ "vars": { "input": "What is 2+2?" },
"scorers": [{ "scorer": "contains", "value": "4" }] },
{ "vars": { "input": "Capital of France?" },
"scorers": [{ "scorer": "icontains", "value": "Paris" }] }
]
}Config Mapping
| Promptfoo | EvalGuard | Note |
|---|---|---|
| promptfooconfig.yaml | evalguard.yaml | Same YAML shape — eval:local reads Promptfoo configs directly |
| providers: | model: | Single model field instead of providers array (first provider is used) |
| tests: | cases: | Either spelling works |
| assert: [{ type: 'contains' }] | scorers: ['contains'] | Per-test assertions become scorers — most types map automatically |
| assert: [{ type: 'llm-rubric' }] | scorers: ['llm-grader'] | Assertion-type names are mapped to EvalGuard scorer names automatically |
| assert: [{ type: 'is-json' }] | scorers: ['json-valid'] | model-graded-factuality → factuality, similar → semantic-similarity, etc. |
| npx promptfoo eval | npx @evalguard/cli eval:local | Same workflow, different CLI |
| npx promptfoo generate redteam | npx @evalguard/cli scan:local | Built-in red team with 300+ attack plugins |
Most assert types map 1:1, but the names differ — EvalGuard handles the rename for you when you run eval:local or import:promptfoo.
Assertions that need manual handling
A handful of assertion types have no 1:1 EvalGuard scorer — the two that run arbitrary user code (javascript / python), plus perplexity-score, classifier, and moderation, which map to a deep-grader scorer instead. eval:local skips the unmapped ones with a warning (it never silently drops them) and import:promptfoolists them in its summary. Here's what to use instead:
Note: Promptfoo's bleu, rouge, and webhook assertions are not in this list — EvalGuard ships built-in bleu, rouge-n, and webhook scorers, so you can reference them directly in your config (e.g. scorers: ['bleu']) instead of rewriting them.
| Promptfoo assertion | EvalGuard equivalent |
|---|---|
| javascript / python | Write a custom scorer — see /docs/scorers/custom-function |
| perplexity-score | EvalGuard scores via a deep-grader rubric instead — see `judge` |
| classifier | Use a deep-grader scorer (e.g. `bias`, `toxicity`) |
| moderation | Use `toxicity` + `pii_leak` deep graders combined |
What you gain by switching
Ready to migrate?
Start with your existing Promptfoo config. No rewrite needed.