Files
wehub-resource-sync 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
chore: import upstream snapshot with attribution
2026-07-13 13:24:08 +08:00

287 lines
8.9 KiB
TypeScript

import { randomUUID } from 'crypto';
import { getDb } from '../../src/database/index';
import { evalsTable } from '../../src/database/tables';
import { ResultFailureReason } from '../../src/index';
import Eval from '../../src/models/eval';
import { oldStyleEval } from './data/eval/database_records';
import type { EvaluateResult } from '../../src/types/index';
interface CreateEvalOptions {
numResults?: number;
resultTypes?: Array<'success' | 'error' | 'failure'>;
withHighlights?: boolean;
withNamedScores?: boolean;
searchableContent?: string;
}
export default class EvalFactory {
static async create(options?: CreateEvalOptions) {
const eval_ = await Eval.create(
{
providers: [{ id: 'test-provider' }],
prompts: ['What is the capital of {{state}}?'],
tests: [
{ vars: { state: 'colorado' }, assert: [{ type: 'contains', value: 'Denver' }] },
{ vars: { state: 'california' }, assert: [{ type: 'contains', value: 'Sacramento' }] },
],
},
[
{ raw: 'What is the capital of california?', label: 'What is the capital of {{state}}?' },
{ raw: 'What is the capital of colorado?', label: 'What is the capital of {{state}}?' },
],
{ id: randomUUID() },
);
await eval_.addPrompts([
{
raw: 'What is the capital of california?',
label: 'What is the capital of {{state}}?',
provider: 'test-provider',
metrics: {
score: 1,
testPassCount: 1,
testFailCount: 1,
testErrorCount: 0,
assertPassCount: 1,
assertFailCount: 1,
totalLatencyMs: 200,
tokenUsage: { total: 20, prompt: 10, completion: 10, cached: 0 },
namedScores: {},
namedScoresCount: {},
redteam: {
pluginPassCount: {},
pluginFailCount: {},
strategyPassCount: {},
strategyFailCount: {},
},
cost: 0.007,
},
},
]);
// If no options are provided, create the default test results
if (!options || options.numResults === undefined) {
await this.addDefaultResults(eval_);
return eval_;
}
// Generate the specified number of test results with the requested characteristics
const numResults = options.numResults === 0 ? 0 : options.numResults || 2;
const resultTypes = options.resultTypes || ['success'];
const withHighlights = options.withHighlights || false;
const withNamedScores = options.withNamedScores || false;
const searchableContent = options.searchableContent || '';
for (let i = 0; i < numResults; i++) {
// Cycle through the result types
const resultTypeIndex = i % resultTypes.length;
const resultType = resultTypes[resultTypeIndex];
// Create a test result based on the type
await this.addCustomResult(eval_, {
testIdx: i,
resultType,
withHighlight: withHighlights && i % 3 === 0, // Add highlights to every 3rd result if enabled
withNamedScores,
searchableContent,
});
}
return eval_;
}
private static async addDefaultResults(eval_: Eval) {
await eval_.addResult({
description: 'test-description',
promptIdx: 0,
testIdx: 0,
testCase: { vars: { state: 'colorado' }, assert: [{ type: 'contains', value: 'Denver' }] },
promptId: 'test-prompt',
provider: { id: 'test-provider', label: 'test-label' },
prompt: {
raw: 'What is the capital of {{state}}?',
label: 'What is the capital of {{state}}?',
},
vars: { state: 'colorado' },
response: {
output: 'denver',
tokenUsage: { total: 10, prompt: 5, completion: 5, cached: 0 },
},
error: null,
failureReason: ResultFailureReason.NONE,
success: true,
score: 1,
latencyMs: 100,
gradingResult: {
pass: true,
score: 1,
reason: 'Expected output "denver" to equal "Denver"',
namedScores: {},
tokensUsed: { total: 10, prompt: 5, completion: 5, cached: 0 },
componentResults: [
{
pass: true,
score: 1,
reason: 'Expected output "denver" to equal "Denver"',
assertion: {
type: 'equals',
value: 'denver',
},
},
],
},
namedScores: {},
cost: 0.007,
metadata: {},
});
await eval_.addResult({
description: 'test-description',
promptIdx: 0,
testIdx: 1,
testCase: {
vars: { state: 'california' },
assert: [{ type: 'contains', value: 'Sacramento' }],
},
promptId: 'test-prompt',
provider: { id: 'test-provider', label: 'test-label' },
prompt: {
raw: 'What is the capital of {{state}}?',
label: 'What is the capital of {{state}}?',
},
vars: { state: 'california' },
response: {
output: 'san francisco',
tokenUsage: { total: 10, prompt: 5, completion: 5, cached: 0 },
},
error: null,
failureReason: ResultFailureReason.NONE,
success: false,
score: 0,
latencyMs: 100,
gradingResult: {
pass: false,
score: 0,
reason: 'Expected output "san francisco" to equal "Sacramento"',
namedScores: {},
tokensUsed: { total: 10, prompt: 5, completion: 5, cached: 0 },
componentResults: [
{
pass: false,
score: 0,
reason: 'Expected output "san francisco" to equal "Sacramento"',
assertion: {
type: 'equals',
value: 'denver',
},
},
],
},
namedScores: {},
cost: 0.007,
metadata: {},
});
}
private static async addCustomResult(
eval_: Eval,
options: {
testIdx: number;
resultType: 'success' | 'error' | 'failure';
withHighlight?: boolean;
withNamedScores?: boolean;
searchableContent?: string;
},
) {
const { testIdx, resultType, withHighlight, withNamedScores, searchableContent } = options;
const stateName = `state${testIdx}`;
const output =
resultType === 'success'
? `Capital of ${stateName} is TestCity${testIdx}`
: `Incorrect answer for ${stateName}${searchableContent ? ` ${searchableContent}` : ''}`;
const result: Partial<EvaluateResult> = {
description: `test-${resultType}-${testIdx}`,
promptIdx: 0,
testIdx,
testCase: {
vars: { state: stateName },
assert: [{ type: 'contains', value: `TestCity${testIdx}` }],
},
promptId: 'test-prompt',
provider: { id: 'test-provider', label: 'test-label' },
prompt: {
raw: `What is the capital of ${stateName}?`,
label: 'What is the capital of {{state}}?',
},
vars: { state: stateName },
response: {
output,
tokenUsage: { total: 10, prompt: 5, completion: 5, cached: 0 },
},
success: resultType === 'success',
score: resultType === 'success' ? 1 : 0,
latencyMs: 100,
namedScores: withNamedScores
? { accuracy: resultType === 'success' ? 1 : 0.2, relevance: 0.8 }
: {},
cost: 0.007,
metadata: {},
};
// Handle error type
if (resultType === 'error') {
result.error = `Error processing ${stateName}: Test error message`;
result.failureReason = ResultFailureReason.ERROR;
} else {
result.error = null;
result.failureReason =
resultType === 'success' ? ResultFailureReason.NONE : ResultFailureReason.ASSERT;
}
// Configure the grading result
if (resultType !== 'error') {
let comment = '';
if (withHighlight) {
comment = `!highlight Important observation for ${stateName}`;
}
result.gradingResult = {
pass: resultType === 'success',
score: resultType === 'success' ? 1 : 0,
reason:
resultType === 'success'
? `Output matches expected value for ${stateName}`
: `Output doesn't match expected value for ${stateName}`,
comment,
namedScores: withNamedScores
? { accuracy: resultType === 'success' ? 1 : 0.2, relevance: 0.8 }
: {},
tokensUsed: { total: 10, prompt: 5, completion: 5, cached: 0 },
componentResults: [
{
pass: resultType === 'success',
score: resultType === 'success' ? 1 : 0,
reason:
resultType === 'success'
? `Expected output matches for ${stateName}`
: `Expected output doesn't match for ${stateName}`,
assertion: {
type: 'equals',
value: `TestCity${testIdx}`,
},
},
],
};
}
await eval_.addResult(result as EvaluateResult);
}
static async createOldResult() {
const db = await getDb();
return (await db.insert(evalsTable).values(oldStyleEval()).returning())[0];
}
}