promptfoo 0.20.0 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/package.json +4 -4
- package/dist/src/assertions.d.ts.map +1 -1
- package/dist/src/assertions.js +5 -0
- package/dist/src/assertions.js.map +1 -1
- package/dist/src/evaluator.js +1 -1
- package/dist/src/evaluator.js.map +1 -1
- package/dist/src/index.d.ts +1 -5
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/matchers.d.ts +3 -2
- package/dist/src/matchers.d.ts.map +1 -1
- package/dist/src/matchers.js +37 -9
- package/dist/src/matchers.js.map +1 -1
- package/dist/src/providers/anthropic.d.ts +5 -3
- package/dist/src/providers/anthropic.d.ts.map +1 -1
- package/dist/src/providers/anthropic.js +8 -10
- package/dist/src/providers/anthropic.js.map +1 -1
- package/dist/src/providers/azureopenai.d.ts +9 -8
- package/dist/src/providers/azureopenai.d.ts.map +1 -1
- package/dist/src/providers/azureopenai.js +33 -36
- package/dist/src/providers/azureopenai.js.map +1 -1
- package/dist/src/providers/openai.d.ts +12 -12
- package/dist/src/providers/openai.d.ts.map +1 -1
- package/dist/src/providers/openai.js +54 -65
- package/dist/src/providers/openai.js.map +1 -1
- package/dist/src/providers/replicate.d.ts +4 -2
- package/dist/src/providers/replicate.d.ts.map +1 -1
- package/dist/src/providers/replicate.js +10 -8
- package/dist/src/providers/replicate.js.map +1 -1
- package/dist/src/providers/webhook.d.ts +9 -0
- package/dist/src/providers/webhook.d.ts.map +1 -0
- package/dist/src/providers/webhook.js +54 -0
- package/dist/src/providers/webhook.js.map +1 -0
- package/dist/src/providers.d.ts +1 -1
- package/dist/src/providers.d.ts.map +1 -1
- package/dist/src/providers.js +36 -28
- package/dist/src/providers.js.map +1 -1
- package/dist/src/suggestions.d.ts.map +1 -1
- package/dist/src/suggestions.js +1 -3
- package/dist/src/suggestions.js.map +1 -1
- package/dist/src/types.d.ts +7 -1
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/util.js +1 -1
- package/dist/src/util.js.map +1 -1
- package/dist/src/web/nextui/404/index.html +1 -1
- package/dist/src/web/nextui/404.html +1 -1
- package/dist/src/web/nextui/_next/static/Bl3o5lF4ON7Fjki46lPhr/_buildManifest.js +1 -0
- package/dist/src/web/nextui/_next/static/chunks/226-7bbb6c98a19542fd.js +37 -0
- package/dist/src/web/nextui/_next/static/chunks/249-ea9c0f034888ccff.js +125 -0
- package/dist/src/web/nextui/_next/static/chunks/339-501c32916b785ef1.js +1 -0
- package/dist/src/web/nextui/_next/static/chunks/365-e426ea5bc7e815fc.js +8 -0
- package/dist/src/web/nextui/_next/static/chunks/396-0a51429a01e24cdd.js +1 -0
- package/dist/src/web/nextui/_next/static/chunks/596-297f7ff4a0436e87.js +25 -0
- package/dist/src/web/nextui/_next/static/chunks/613-572c22424de64659.js +1 -0
- package/dist/src/web/nextui/_next/static/chunks/706-ae1d3352d28419e9.js +9 -0
- package/dist/src/web/nextui/_next/static/chunks/891-7035926a62c1c4e0.js +1 -0
- package/dist/src/web/nextui/_next/static/chunks/app/eval/[id]/not-found-366629541fd598e9.js +1 -0
- package/dist/src/web/nextui/_next/static/chunks/app/eval/[id]/page-319d2ee38d37574e.js +1 -0
- package/dist/src/web/nextui/_next/static/chunks/app/eval/page-a6b1ff91723b7beb.js +1 -0
- package/dist/src/web/nextui/_next/static/chunks/app/layout-024c4adc71c9feb0.js +1 -0
- package/dist/src/web/nextui/_next/static/chunks/app/page-1ae60660130041b2.js +1 -0
- package/dist/src/web/nextui/_next/static/chunks/app/setup/page-6ef16148040bf4f4.js +1 -0
- package/dist/src/web/nextui/_next/static/chunks/{ca377847-cb6ae6a6a073aebb.js → ca377847-26b462611379a4f7.js} +3 -3
- package/dist/src/web/nextui/_next/static/chunks/{fd9d1056-ac777be631f5a9e9.js → fd9d1056-fba4b53a2f01213b.js} +1 -1
- package/dist/src/web/nextui/_next/static/chunks/framework-8883d1e9be70c3da.js +25 -0
- package/dist/src/web/nextui/_next/static/chunks/main-8ea85465d428ecfe.js +1 -0
- package/dist/src/web/nextui/_next/static/chunks/main-app-581ccf0003955b21.js +1 -0
- package/dist/src/web/nextui/_next/static/chunks/pages/_app-52924524f99094ab.js +1 -0
- package/dist/src/web/nextui/_next/static/chunks/pages/_error-c92d5c4bb2b49926.js +1 -0
- package/dist/src/web/nextui/_next/static/chunks/webpack-55c264ce2fd85eb7.js +1 -0
- package/dist/src/web/nextui/_next/static/css/4d399fceacd06992.css +1 -0
- package/dist/src/web/nextui/eval/index.html +1 -1
- package/dist/src/web/nextui/eval/index.txt +6 -6
- package/dist/src/web/nextui/index.html +1 -1
- package/dist/src/web/nextui/index.txt +5 -5
- package/dist/src/web/nextui/setup/index.html +27 -1
- package/dist/src/web/nextui/setup/index.txt +9 -9
- package/dist/src/web/server.d.ts.map +1 -1
- package/dist/src/web/server.js +9 -5
- package/dist/src/web/server.js.map +1 -1
- package/package.json +4 -4
- package/dist/src/web/nextui/_next/static/US6gOx8LHTX_Hzm9aYNrC/_buildManifest.js +0 -1
- package/dist/src/web/nextui/_next/static/chunks/339-4fc8a80fa840e771.js +0 -1
- package/dist/src/web/nextui/_next/static/chunks/373-8a280796c0f2d1af.js +0 -1
- package/dist/src/web/nextui/_next/static/chunks/583-125d32af505e9bc4.js +0 -1
- package/dist/src/web/nextui/_next/static/chunks/596-07e4a23a5c6cdf04.js +0 -25
- package/dist/src/web/nextui/_next/static/chunks/658-a62210d07dc4dcb6.js +0 -15
- package/dist/src/web/nextui/_next/static/chunks/707-699cbd84b259c37b.js +0 -37
- package/dist/src/web/nextui/_next/static/chunks/858-ceb6fa22e614492b.js +0 -125
- package/dist/src/web/nextui/_next/static/chunks/891-3000ea7c0a292558.js +0 -1
- package/dist/src/web/nextui/_next/static/chunks/app/eval/[id]/not-found-50e40614fa05600e.js +0 -1
- package/dist/src/web/nextui/_next/static/chunks/app/eval/[id]/page-c19c44ed1b2dfb58.js +0 -1
- package/dist/src/web/nextui/_next/static/chunks/app/eval/page-d4a1813b2f8c4532.js +0 -1
- package/dist/src/web/nextui/_next/static/chunks/app/layout-664a8d716d2d24b1.js +0 -1
- package/dist/src/web/nextui/_next/static/chunks/app/page-1f8ef6a00a2355f0.js +0 -1
- package/dist/src/web/nextui/_next/static/chunks/app/setup/page-182018a3c6397345.js +0 -1
- package/dist/src/web/nextui/_next/static/chunks/framework-43665103d101a22d.js +0 -25
- package/dist/src/web/nextui/_next/static/chunks/main-50cc0a98559591ce.js +0 -1
- package/dist/src/web/nextui/_next/static/chunks/main-app-c9dc13756d166550.js +0 -1
- package/dist/src/web/nextui/_next/static/chunks/pages/_app-6b79a29ad0d63b21.js +0 -1
- package/dist/src/web/nextui/_next/static/chunks/pages/_error-9aeb3e4d490fe4b8.js +0 -1
- package/dist/src/web/nextui/_next/static/chunks/webpack-6e474e42be502dd7.js +0 -1
- package/dist/src/web/nextui/_next/static/css/a35c840ac696f161.css +0 -1
- package/dist/src/web/nextui/api +0 -1
- package/src/__mocks__/esm.ts +0 -3
- package/src/assertions.ts +0 -580
- package/src/cache.ts +0 -109
- package/src/esm.ts +0 -13
- package/src/evaluator.ts +0 -500
- package/src/index.ts +0 -52
- package/src/logger.ts +0 -46
- package/src/main.ts +0 -442
- package/src/matchers.ts +0 -120
- package/src/onboarding.ts +0 -69
- package/src/prompts.ts +0 -39
- package/src/providers/anthropic.ts +0 -88
- package/src/providers/azureopenai.ts +0 -299
- package/src/providers/llama.ts +0 -95
- package/src/providers/localai.ts +0 -111
- package/src/providers/ollama.ts +0 -89
- package/src/providers/openai.ts +0 -337
- package/src/providers/replicate.ts +0 -99
- package/src/providers/scriptCompletion.ts +0 -35
- package/src/providers/shared.ts +0 -34
- package/src/providers.ts +0 -192
- package/src/share.ts +0 -27
- package/src/suggestions.ts +0 -63
- package/src/table.ts +0 -43
- package/src/tableOutput.html +0 -52
- package/src/telemetry.ts +0 -70
- package/src/types.ts +0 -299
- package/src/updates.ts +0 -46
- package/src/util.ts +0 -543
- package/src/web/nextui/.eslintrc.json +0 -3
- package/src/web/nextui/next.config.js +0 -14
- package/src/web/nextui/package-lock.json +0 -4644
- package/src/web/nextui/package.json +0 -47
- package/src/web/nextui/public/favicon.ico +0 -0
- package/src/web/nextui/public/logo.svg +0 -30
- package/src/web/nextui/src/app/Home.css +0 -3
- package/src/web/nextui/src/app/api/route.ts +0 -6
- package/src/web/nextui/src/app/components/DarkMode.css +0 -22
- package/src/web/nextui/src/app/components/DarkMode.tsx +0 -17
- package/src/web/nextui/src/app/components/Logo.css +0 -32
- package/src/web/nextui/src/app/components/Logo.tsx +0 -11
- package/src/web/nextui/src/app/components/PageShell.css +0 -33
- package/src/web/nextui/src/app/components/PageShell.tsx +0 -87
- package/src/web/nextui/src/app/eval/ConfigModal.tsx +0 -84
- package/src/web/nextui/src/app/eval/Eval.css +0 -13
- package/src/web/nextui/src/app/eval/Eval.tsx +0 -79
- package/src/web/nextui/src/app/eval/EvalOutputPromptDialog.tsx +0 -127
- package/src/web/nextui/src/app/eval/ResultsCharts.tsx +0 -355
- package/src/web/nextui/src/app/eval/ResultsTable.css +0 -179
- package/src/web/nextui/src/app/eval/ResultsTable.tsx +0 -503
- package/src/web/nextui/src/app/eval/ResultsView.tsx +0 -301
- package/src/web/nextui/src/app/eval/ShareModal.tsx +0 -70
- package/src/web/nextui/src/app/eval/[id]/not-found.tsx +0 -5
- package/src/web/nextui/src/app/eval/[id]/page.css +0 -9
- package/src/web/nextui/src/app/eval/[id]/page.tsx +0 -20
- package/src/web/nextui/src/app/eval/index.css +0 -0
- package/src/web/nextui/src/app/eval/page.tsx +0 -8
- package/src/web/nextui/src/app/eval/store.ts +0 -18
- package/src/web/nextui/src/app/eval/types.ts +0 -20
- package/src/web/nextui/src/app/globals.css +0 -58
- package/src/web/nextui/src/app/layout.tsx +0 -25
- package/src/web/nextui/src/app/page.tsx +0 -7
- package/src/web/nextui/src/app/setup/AssertsForm.tsx +0 -118
- package/src/web/nextui/src/app/setup/PromptDialog.tsx +0 -77
- package/src/web/nextui/src/app/setup/PromptsSection.tsx +0 -190
- package/src/web/nextui/src/app/setup/ProviderConfigDialog.tsx +0 -99
- package/src/web/nextui/src/app/setup/ProviderSelector.tsx +0 -149
- package/src/web/nextui/src/app/setup/RunTestSuiteButton.tsx +0 -88
- package/src/web/nextui/src/app/setup/TestCaseDialog.tsx +0 -108
- package/src/web/nextui/src/app/setup/TestCasesSection.tsx +0 -154
- package/src/web/nextui/src/app/setup/VarsForm.tsx +0 -57
- package/src/web/nextui/src/app/setup/page.css +0 -3
- package/src/web/nextui/src/app/setup/page.tsx +0 -160
- package/src/web/nextui/src/util/api.ts +0 -1
- package/src/web/nextui/src/util/store.ts +0 -53
- package/src/web/nextui/tsconfig.json +0 -28
- package/src/web/server.ts +0 -151
- /package/dist/src/web/nextui/_next/static/{US6gOx8LHTX_Hzm9aYNrC → Bl3o5lF4ON7Fjki46lPhr}/_ssgManifest.js +0 -0
|
@@ -1,301 +0,0 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
|
|
3
|
-
import invariant from 'tiny-invariant';
|
|
4
|
-
import Button from '@mui/material/Button';
|
|
5
|
-
import Box from '@mui/material/Box';
|
|
6
|
-
import Checkbox from '@mui/material/Checkbox';
|
|
7
|
-
import CircularProgress from '@mui/material/CircularProgress';
|
|
8
|
-
import FormControl from '@mui/material/FormControl';
|
|
9
|
-
import FormControlLabel from '@mui/material/FormControlLabel';
|
|
10
|
-
import InputLabel from '@mui/material/InputLabel';
|
|
11
|
-
import ListItemText from '@mui/material/ListItemText';
|
|
12
|
-
import MenuItem from '@mui/material/MenuItem';
|
|
13
|
-
import OutlinedInput from '@mui/material/OutlinedInput';
|
|
14
|
-
import Paper from '@mui/material/Box';
|
|
15
|
-
import Select, { SelectChangeEvent } from '@mui/material/Select';
|
|
16
|
-
import Slider from '@mui/material/Slider';
|
|
17
|
-
import Stack from '@mui/material/Stack';
|
|
18
|
-
import Tooltip from '@mui/material/Tooltip';
|
|
19
|
-
import Typography from '@mui/material/Typography';
|
|
20
|
-
import ShareIcon from '@mui/icons-material/Share';
|
|
21
|
-
import VisibilityIcon from '@mui/icons-material/Visibility';
|
|
22
|
-
import { styled } from '@mui/system';
|
|
23
|
-
|
|
24
|
-
import ResultsCharts from './ResultsCharts';
|
|
25
|
-
import ResultsTable from './ResultsTable';
|
|
26
|
-
import ConfigModal from './ConfigModal';
|
|
27
|
-
import ShareModal from './ShareModal';
|
|
28
|
-
import { useStore } from './store';
|
|
29
|
-
|
|
30
|
-
import type { VisibilityState } from '@tanstack/table-core';
|
|
31
|
-
import type { FilterMode } from './types';
|
|
32
|
-
|
|
33
|
-
const ResponsiveStack = styled(Stack)(({ theme }) => ({
|
|
34
|
-
maxWidth: '100%',
|
|
35
|
-
flexWrap: 'wrap',
|
|
36
|
-
[theme.breakpoints.down('sm')]: {
|
|
37
|
-
flexDirection: 'column',
|
|
38
|
-
},
|
|
39
|
-
}));
|
|
40
|
-
|
|
41
|
-
function filenameToDate(filename: string) {
|
|
42
|
-
const dateString = filename.slice('eval-'.length, filename.length - '.json'.length);
|
|
43
|
-
|
|
44
|
-
// Replace hyphens with colons where necessary (Windows compatibility).
|
|
45
|
-
const dateParts = dateString.split('T');
|
|
46
|
-
const timePart = dateParts[1].replace(/-/g, ':');
|
|
47
|
-
const formattedDateString = `${dateParts[0]}T${timePart}`;
|
|
48
|
-
|
|
49
|
-
const date = new Date(formattedDateString);
|
|
50
|
-
return date.toLocaleDateString('en-US', {
|
|
51
|
-
year: 'numeric',
|
|
52
|
-
month: 'long',
|
|
53
|
-
day: 'numeric',
|
|
54
|
-
hour: '2-digit',
|
|
55
|
-
minute: '2-digit',
|
|
56
|
-
second: '2-digit',
|
|
57
|
-
timeZoneName: 'short',
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
interface ResultsViewProps {
|
|
62
|
-
recentFiles: string[];
|
|
63
|
-
onRecentFileSelected: (file: string) => void;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export default function ResultsView({ recentFiles, onRecentFileSelected }: ResultsViewProps) {
|
|
67
|
-
const { table, config } = useStore();
|
|
68
|
-
const [maxTextLength, setMaxTextLength] = React.useState(250);
|
|
69
|
-
const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({});
|
|
70
|
-
const [selectedColumns, setSelectedColumns] = React.useState<string[]>([]);
|
|
71
|
-
|
|
72
|
-
const [failureFilter, setFailureFilter] = React.useState<{ [key: string]: boolean }>({});
|
|
73
|
-
const handleFailureFilterToggle = (columnId: string, checked: boolean) => {
|
|
74
|
-
setFailureFilter((prevFailureFilter) => ({ ...prevFailureFilter, [columnId]: checked }));
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
const [filterMode, setFilterMode] = React.useState<FilterMode>('all');
|
|
78
|
-
const handleFilterModeChange = (event: SelectChangeEvent<unknown>) => {
|
|
79
|
-
const mode = event.target.value as FilterMode;
|
|
80
|
-
setFilterMode(mode);
|
|
81
|
-
|
|
82
|
-
const newFailureFilter: { [key: string]: boolean } = {};
|
|
83
|
-
head.prompts.forEach((_, idx) => {
|
|
84
|
-
const columnId = `Prompt ${idx + 1}`;
|
|
85
|
-
newFailureFilter[columnId] = mode === 'failures';
|
|
86
|
-
});
|
|
87
|
-
setFailureFilter(newFailureFilter);
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
const [wordBreak, setWordBreak] = React.useState<'break-word' | 'break-all'>('break-word');
|
|
91
|
-
const handleWordBreakChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
92
|
-
setWordBreak(event.target.checked ? 'break-all' : 'break-word');
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
const [shareModalOpen, setShareModalOpen] = React.useState(false);
|
|
96
|
-
const [shareUrl, setShareUrl] = React.useState('');
|
|
97
|
-
const [shareLoading, setShareLoading] = React.useState(false);
|
|
98
|
-
|
|
99
|
-
const handleShareButtonClick = async () => {
|
|
100
|
-
setShareLoading(true);
|
|
101
|
-
try {
|
|
102
|
-
const response = await fetch('https://api.promptfoo.dev/eval', {
|
|
103
|
-
method: 'POST',
|
|
104
|
-
headers: {
|
|
105
|
-
'Content-Type': 'application/json',
|
|
106
|
-
},
|
|
107
|
-
body: JSON.stringify({
|
|
108
|
-
data: {
|
|
109
|
-
version: 1,
|
|
110
|
-
results: {
|
|
111
|
-
table,
|
|
112
|
-
},
|
|
113
|
-
config,
|
|
114
|
-
},
|
|
115
|
-
}),
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
const { id } = await response.json();
|
|
119
|
-
const shareUrl = `https://app.promptfoo.dev/eval/${id}`;
|
|
120
|
-
setShareUrl(shareUrl);
|
|
121
|
-
setShareModalOpen(true);
|
|
122
|
-
} catch {
|
|
123
|
-
alert('Sorry, something went wrong.');
|
|
124
|
-
} finally {
|
|
125
|
-
setShareLoading(false);
|
|
126
|
-
}
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
const [configModalOpen, setConfigModalOpen] = React.useState(false);
|
|
130
|
-
|
|
131
|
-
invariant(table, 'Table data must be loaded before rendering ResultsView');
|
|
132
|
-
const { head } = table;
|
|
133
|
-
|
|
134
|
-
const handleChange = (event: SelectChangeEvent<typeof selectedColumns>) => {
|
|
135
|
-
const {
|
|
136
|
-
target: { value },
|
|
137
|
-
} = event;
|
|
138
|
-
setSelectedColumns(typeof value === 'string' ? value.split(',') : value);
|
|
139
|
-
|
|
140
|
-
const allColumns = [
|
|
141
|
-
...head.vars.map((_, idx) => `Variable ${idx + 1}`),
|
|
142
|
-
...head.prompts.map((_, idx) => `Prompt ${idx + 1}`),
|
|
143
|
-
];
|
|
144
|
-
const newColumnVisibility: VisibilityState = {};
|
|
145
|
-
allColumns.forEach((col) => {
|
|
146
|
-
newColumnVisibility[col] = (typeof value === 'string' ? value.split(',') : value).includes(
|
|
147
|
-
col,
|
|
148
|
-
);
|
|
149
|
-
});
|
|
150
|
-
setColumnVisibility(newColumnVisibility);
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
const columnData = [
|
|
154
|
-
...head.vars.map((_, idx) => ({
|
|
155
|
-
value: `Variable ${idx + 1}`,
|
|
156
|
-
label: `Variable ${idx + 1}`,
|
|
157
|
-
group: 'Variables',
|
|
158
|
-
})),
|
|
159
|
-
...head.prompts.map((_, idx) => ({
|
|
160
|
-
value: `Prompt ${idx + 1}`,
|
|
161
|
-
label: `Prompt ${idx + 1}`,
|
|
162
|
-
group: 'Prompts',
|
|
163
|
-
})),
|
|
164
|
-
];
|
|
165
|
-
|
|
166
|
-
// Set all columns as selected by default
|
|
167
|
-
React.useEffect(() => {
|
|
168
|
-
setSelectedColumns([
|
|
169
|
-
...head.vars.map((_, idx) => `Variable ${idx + 1}`),
|
|
170
|
-
...head.prompts.map((_, idx) => `Prompt ${idx + 1}`),
|
|
171
|
-
]);
|
|
172
|
-
}, [head]);
|
|
173
|
-
|
|
174
|
-
return (
|
|
175
|
-
<div style={{ marginLeft: '1rem', marginRight: '1rem' }}>
|
|
176
|
-
<Paper py="md">
|
|
177
|
-
<ResponsiveStack direction="row" spacing={4} alignItems="center">
|
|
178
|
-
<Box>
|
|
179
|
-
{recentFiles && recentFiles.length > 0 && (
|
|
180
|
-
<FormControl sx={{ m: 1, minWidth: 200 }} size="small">
|
|
181
|
-
<InputLabel>View run</InputLabel>
|
|
182
|
-
<Select
|
|
183
|
-
key={recentFiles.join(',')}
|
|
184
|
-
className="recent-files"
|
|
185
|
-
label="Previous runs"
|
|
186
|
-
defaultValue={recentFiles[0]}
|
|
187
|
-
onChange={(e: SelectChangeEvent) => onRecentFileSelected(e.target.value)}
|
|
188
|
-
>
|
|
189
|
-
{recentFiles.map((file) => (
|
|
190
|
-
<MenuItem key={file} value={file}>
|
|
191
|
-
{filenameToDate(file)}
|
|
192
|
-
</MenuItem>
|
|
193
|
-
))}
|
|
194
|
-
</Select>
|
|
195
|
-
</FormControl>
|
|
196
|
-
)}
|
|
197
|
-
</Box>
|
|
198
|
-
<Box>
|
|
199
|
-
<FormControl sx={{ m: 1, minWidth: 200 }} size="small">
|
|
200
|
-
<InputLabel id="visible-columns-label">Show columns</InputLabel>
|
|
201
|
-
<Select
|
|
202
|
-
labelId="visible-columns-label"
|
|
203
|
-
id="visible-columns"
|
|
204
|
-
multiple
|
|
205
|
-
value={selectedColumns}
|
|
206
|
-
onChange={handleChange}
|
|
207
|
-
input={<OutlinedInput label="Visible columns" />}
|
|
208
|
-
renderValue={(selected: string[]) => selected.join(', ')}
|
|
209
|
-
>
|
|
210
|
-
{columnData.map((column) => (
|
|
211
|
-
<MenuItem dense key={column.value} value={column.value}>
|
|
212
|
-
<Checkbox checked={selectedColumns.indexOf(column.value) > -1} />
|
|
213
|
-
<ListItemText primary={column.label} />
|
|
214
|
-
</MenuItem>
|
|
215
|
-
))}
|
|
216
|
-
</Select>
|
|
217
|
-
</FormControl>
|
|
218
|
-
</Box>
|
|
219
|
-
<Box>
|
|
220
|
-
<FormControl sx={{ minWidth: 180 }} size="small">
|
|
221
|
-
<InputLabel id="failure-filter-mode-label">Filter</InputLabel>
|
|
222
|
-
<Select
|
|
223
|
-
labelId="filter-mode-label"
|
|
224
|
-
id="filter-mode"
|
|
225
|
-
value={filterMode}
|
|
226
|
-
onChange={handleFilterModeChange}
|
|
227
|
-
label="Filter"
|
|
228
|
-
>
|
|
229
|
-
<MenuItem value="all">Show all results</MenuItem>
|
|
230
|
-
<MenuItem value="failures">Show failures only</MenuItem>
|
|
231
|
-
<MenuItem value="different">Show different only</MenuItem>
|
|
232
|
-
</Select>
|
|
233
|
-
</FormControl>
|
|
234
|
-
</Box>
|
|
235
|
-
<Box>
|
|
236
|
-
<Typography mt={2}>Max text length: {maxTextLength}</Typography>
|
|
237
|
-
<Slider
|
|
238
|
-
min={25}
|
|
239
|
-
max={1000}
|
|
240
|
-
value={maxTextLength}
|
|
241
|
-
onChange={(_, val: number | number[]) => setMaxTextLength(val as number)}
|
|
242
|
-
/>
|
|
243
|
-
</Box>
|
|
244
|
-
<Box>
|
|
245
|
-
<Tooltip title="Forcing line breaks makes it easier to adjust column widths to your liking">
|
|
246
|
-
<FormControlLabel
|
|
247
|
-
control={
|
|
248
|
-
<Checkbox checked={wordBreak === 'break-all'} onChange={handleWordBreakChange} />
|
|
249
|
-
}
|
|
250
|
-
label="Force line breaks"
|
|
251
|
-
/>
|
|
252
|
-
</Tooltip>
|
|
253
|
-
</Box>
|
|
254
|
-
<Box flexGrow={1} />
|
|
255
|
-
<Box display="flex" justifyContent="flex-end">
|
|
256
|
-
<ResponsiveStack direction="row" spacing={2}>
|
|
257
|
-
{config && (
|
|
258
|
-
<Tooltip title="View config">
|
|
259
|
-
<Button
|
|
260
|
-
color="primary"
|
|
261
|
-
onClick={() => setConfigModalOpen(true)}
|
|
262
|
-
startIcon={<VisibilityIcon />}
|
|
263
|
-
>
|
|
264
|
-
Config
|
|
265
|
-
</Button>
|
|
266
|
-
</Tooltip>
|
|
267
|
-
)}
|
|
268
|
-
{config?.sharing && (
|
|
269
|
-
<Tooltip title="Generate a unique URL that others can access">
|
|
270
|
-
<Button
|
|
271
|
-
color="primary"
|
|
272
|
-
onClick={handleShareButtonClick}
|
|
273
|
-
disabled={shareLoading}
|
|
274
|
-
startIcon={shareLoading ? <CircularProgress size={16} /> : <ShareIcon />}
|
|
275
|
-
>
|
|
276
|
-
Share
|
|
277
|
-
</Button>
|
|
278
|
-
</Tooltip>
|
|
279
|
-
)}
|
|
280
|
-
</ResponsiveStack>
|
|
281
|
-
</Box>
|
|
282
|
-
</ResponsiveStack>
|
|
283
|
-
</Paper>
|
|
284
|
-
<ResultsCharts columnVisibility={columnVisibility} />
|
|
285
|
-
<ResultsTable
|
|
286
|
-
maxTextLength={maxTextLength}
|
|
287
|
-
columnVisibility={columnVisibility}
|
|
288
|
-
wordBreak={wordBreak}
|
|
289
|
-
filterMode={filterMode}
|
|
290
|
-
failureFilter={failureFilter}
|
|
291
|
-
onFailureFilterToggle={handleFailureFilterToggle}
|
|
292
|
-
/>
|
|
293
|
-
<ConfigModal open={configModalOpen} onClose={() => setConfigModalOpen(false)} />
|
|
294
|
-
<ShareModal
|
|
295
|
-
open={shareModalOpen}
|
|
296
|
-
onClose={() => setShareModalOpen(false)}
|
|
297
|
-
shareUrl={shareUrl}
|
|
298
|
-
/>
|
|
299
|
-
</div>
|
|
300
|
-
);
|
|
301
|
-
}
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import React, { useRef, useState } from 'react';
|
|
2
|
-
import Dialog from '@mui/material/Dialog';
|
|
3
|
-
import DialogTitle from '@mui/material/DialogTitle';
|
|
4
|
-
import DialogContent from '@mui/material/DialogContent';
|
|
5
|
-
import DialogContentText from '@mui/material/DialogContentText';
|
|
6
|
-
import DialogActions from '@mui/material/DialogActions';
|
|
7
|
-
import Button from '@mui/material/Button';
|
|
8
|
-
import TextField from '@mui/material/TextField';
|
|
9
|
-
import IconButton from '@mui/material/IconButton';
|
|
10
|
-
import FileCopyIcon from '@mui/icons-material/FileCopy';
|
|
11
|
-
import CheckIcon from '@mui/icons-material/Check';
|
|
12
|
-
|
|
13
|
-
interface ShareModalProps {
|
|
14
|
-
open: boolean;
|
|
15
|
-
onClose: () => void;
|
|
16
|
-
shareUrl: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const ShareModal: React.FC<ShareModalProps> = ({ open, onClose, shareUrl }) => {
|
|
20
|
-
const inputRef = useRef<HTMLInputElement>(null);
|
|
21
|
-
const [copied, setCopied] = useState(false);
|
|
22
|
-
|
|
23
|
-
const handleCopyClick = () => {
|
|
24
|
-
if (inputRef.current) {
|
|
25
|
-
inputRef.current.select();
|
|
26
|
-
document.execCommand('copy');
|
|
27
|
-
setCopied(true);
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
const handleClose = () => {
|
|
32
|
-
onClose();
|
|
33
|
-
setCopied(false);
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
return (
|
|
37
|
-
<Dialog
|
|
38
|
-
open={open}
|
|
39
|
-
onClose={handleClose}
|
|
40
|
-
PaperProps={{ style: { minWidth: 'min(660px, 100%)' } }}
|
|
41
|
-
>
|
|
42
|
-
<DialogTitle>Your eval is ready to share</DialogTitle>
|
|
43
|
-
<DialogContent>
|
|
44
|
-
<TextField
|
|
45
|
-
inputRef={inputRef}
|
|
46
|
-
value={shareUrl}
|
|
47
|
-
fullWidth
|
|
48
|
-
InputProps={{
|
|
49
|
-
readOnly: true,
|
|
50
|
-
endAdornment: (
|
|
51
|
-
<IconButton onClick={handleCopyClick}>
|
|
52
|
-
{copied ? <CheckIcon /> : <FileCopyIcon />}
|
|
53
|
-
</IconButton>
|
|
54
|
-
),
|
|
55
|
-
}}
|
|
56
|
-
/>
|
|
57
|
-
<DialogContentText sx={{ fontSize: '0.75rem' }}>
|
|
58
|
-
Shared URLs are deleted after 1 week.
|
|
59
|
-
</DialogContentText>
|
|
60
|
-
</DialogContent>
|
|
61
|
-
<DialogActions>
|
|
62
|
-
<Button onClick={handleClose} color="primary">
|
|
63
|
-
Close
|
|
64
|
-
</Button>
|
|
65
|
-
</DialogActions>
|
|
66
|
-
</Dialog>
|
|
67
|
-
);
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
export default ShareModal;
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { notFound } from 'next/navigation';
|
|
3
|
-
|
|
4
|
-
import Eval from '../Eval';
|
|
5
|
-
|
|
6
|
-
import './page.css';
|
|
7
|
-
|
|
8
|
-
export async function generateStaticParams() {
|
|
9
|
-
return [];
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export default async function Page({ params }: { params: { id: string } }) {
|
|
13
|
-
const response = await fetch(`https://api.promptfoo.dev/eval/${params.id}`);
|
|
14
|
-
if (!response.ok) {
|
|
15
|
-
notFound();
|
|
16
|
-
}
|
|
17
|
-
const data = await response.json();
|
|
18
|
-
|
|
19
|
-
return <Eval preloadedData={data} />;
|
|
20
|
-
}
|
|
File without changes
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { create } from 'zustand';
|
|
2
|
-
|
|
3
|
-
import type { EvalTable, UnifiedConfig } from './types';
|
|
4
|
-
|
|
5
|
-
interface TableState {
|
|
6
|
-
table: EvalTable | null;
|
|
7
|
-
setTable: (table: EvalTable | null) => void;
|
|
8
|
-
|
|
9
|
-
config: Partial<UnifiedConfig> | null;
|
|
10
|
-
setConfig: (config: Partial<UnifiedConfig> | null) => void;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export const useStore = create<TableState>((set) => ({
|
|
14
|
-
table: null,
|
|
15
|
-
setTable: (table: EvalTable | null) => set(() => ({ table })),
|
|
16
|
-
config: null,
|
|
17
|
-
setConfig: (config: Partial<UnifiedConfig> | null) => set(() => ({ config })),
|
|
18
|
-
}));
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { Prompt, EvaluateTableOutput } from '../../../../../types';
|
|
2
|
-
|
|
3
|
-
export type EvalHead = {
|
|
4
|
-
prompts: Prompt[];
|
|
5
|
-
vars: string[];
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
export type EvalRow = {
|
|
9
|
-
outputs: EvaluateTableOutput[];
|
|
10
|
-
vars: string[]; // model outputs
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export type EvalTable = {
|
|
14
|
-
head: EvalHead;
|
|
15
|
-
body: EvalRow[];
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export type FilterMode = 'all' | 'failures' | 'different';
|
|
19
|
-
|
|
20
|
-
export * from '../../../../../types';
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
/* This CSS is common to all pages */
|
|
2
|
-
|
|
3
|
-
* {
|
|
4
|
-
box-sizing: border-box;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
:root {
|
|
8
|
-
font-synthesis: none;
|
|
9
|
-
text-rendering: optimizeLegibility;
|
|
10
|
-
-webkit-font-smoothing: antialiased;
|
|
11
|
-
-moz-osx-font-smoothing: grayscale;
|
|
12
|
-
-webkit-text-size-adjust: 100%;
|
|
13
|
-
|
|
14
|
-
/* Light mode colors */
|
|
15
|
-
--background-color: #ffffff;
|
|
16
|
-
--text-color: #404040;
|
|
17
|
-
--border-color: lightgray;
|
|
18
|
-
--table-border-color: lightgray;
|
|
19
|
-
--pass-color: green;
|
|
20
|
-
--fail-color: #ad0000;
|
|
21
|
-
--smalltext-color: gray;
|
|
22
|
-
--success-background-color: #d1ffd7;
|
|
23
|
-
--variable-background-color: #f7f7f7;
|
|
24
|
-
--header-background-color: #fffdf7;
|
|
25
|
-
--insert-highlight-color: #d4fcbc;
|
|
26
|
-
--delete-highlight-color: #fbb6c2;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/* Dark mode colors */
|
|
30
|
-
[data-theme='dark'] {
|
|
31
|
-
/* Keep synced with prefers-color-scheme above */
|
|
32
|
-
--background-color: #1a1a1a;
|
|
33
|
-
--text-color: #f0f0f0;
|
|
34
|
-
--border-color: #444444;
|
|
35
|
-
--table-border-color: #444444;
|
|
36
|
-
--pass-color: #4caf50;
|
|
37
|
-
--fail-color: #f44336;
|
|
38
|
-
--smalltext-color: #888888;
|
|
39
|
-
--success-background-color: #216d2b;
|
|
40
|
-
--variable-background-color: #333;
|
|
41
|
-
--header-background-color: #333;
|
|
42
|
-
--insert-highlight-color: #4f8a34;
|
|
43
|
-
--delete-highlight-color: #8a3434;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
html {
|
|
47
|
-
font-size: 16px;
|
|
48
|
-
background-color: var(--background-color);
|
|
49
|
-
color: var(--text-color);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
body {
|
|
53
|
-
margin: 0;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
* {
|
|
57
|
-
box-sizing: border-box;
|
|
58
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import './globals.css';
|
|
2
|
-
import type { Metadata } from 'next';
|
|
3
|
-
import { Roboto } from 'next/font/google';
|
|
4
|
-
import { PageShell } from './components/PageShell';
|
|
5
|
-
|
|
6
|
-
const roboto = Roboto({
|
|
7
|
-
weight: ['400', '500', '700'],
|
|
8
|
-
style: ['normal'],
|
|
9
|
-
subsets: ['latin'],
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
export const metadata: Metadata = {
|
|
13
|
-
title: 'promptfoo',
|
|
14
|
-
description: 'LLM testing and evaluation',
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
18
|
-
return (
|
|
19
|
-
<html lang="en">
|
|
20
|
-
<body className={roboto.className}>
|
|
21
|
-
<PageShell>{children}</PageShell>
|
|
22
|
-
</body>
|
|
23
|
-
</html>
|
|
24
|
-
);
|
|
25
|
-
}
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
// src/components/AssertsForm.tsx
|
|
2
|
-
import React, { useState } from 'react';
|
|
3
|
-
import Autocomplete from '@mui/material/Autocomplete';
|
|
4
|
-
import Box from '@mui/material/Box';
|
|
5
|
-
import Button from '@mui/material/Button';
|
|
6
|
-
import Delete from '@mui/icons-material/Delete';
|
|
7
|
-
import IconButton from '@mui/material/IconButton';
|
|
8
|
-
import Stack from '@mui/material/Stack';
|
|
9
|
-
import TextField from '@mui/material/TextField';
|
|
10
|
-
import Typography from '@mui/material/Typography';
|
|
11
|
-
import type { Assertion, AssertionType } from '../../../../../types';
|
|
12
|
-
|
|
13
|
-
interface AssertsFormProps {
|
|
14
|
-
onAdd: (asserts: Assertion[]) => void;
|
|
15
|
-
initialValues: Assertion[];
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const assertTypes: AssertionType[] = [
|
|
19
|
-
'equals',
|
|
20
|
-
'contains',
|
|
21
|
-
'icontains',
|
|
22
|
-
'contains-all',
|
|
23
|
-
'contains-any',
|
|
24
|
-
'starts-with',
|
|
25
|
-
'regex',
|
|
26
|
-
'is-json',
|
|
27
|
-
'contains-json',
|
|
28
|
-
'javascript',
|
|
29
|
-
'python',
|
|
30
|
-
'similar',
|
|
31
|
-
'llm-rubric',
|
|
32
|
-
'webhook',
|
|
33
|
-
'rouge-n',
|
|
34
|
-
'rouge-s',
|
|
35
|
-
'rouge-l',
|
|
36
|
-
'not-equals',
|
|
37
|
-
'not-contains',
|
|
38
|
-
'not-icontains',
|
|
39
|
-
'not-contains-all',
|
|
40
|
-
'not-contains-any',
|
|
41
|
-
'not-starts-with',
|
|
42
|
-
'not-regex',
|
|
43
|
-
'not-is-json',
|
|
44
|
-
'not-contains-json',
|
|
45
|
-
'not-javascript',
|
|
46
|
-
'not-python',
|
|
47
|
-
'not-similar',
|
|
48
|
-
'not-llm-rubric',
|
|
49
|
-
'not-webhook',
|
|
50
|
-
'not-rouge-n',
|
|
51
|
-
'not-rouge-s',
|
|
52
|
-
'not-rouge-l',
|
|
53
|
-
];
|
|
54
|
-
|
|
55
|
-
const AssertsForm: React.FC<AssertsFormProps> = ({ onAdd, initialValues }) => {
|
|
56
|
-
const [asserts, setAsserts] = useState<Assertion[]>(initialValues || []);
|
|
57
|
-
|
|
58
|
-
const handleAdd = () => {
|
|
59
|
-
const newAsserts = [...asserts, { type: 'equals' as AssertionType, value: '' }];
|
|
60
|
-
setAsserts(newAsserts);
|
|
61
|
-
onAdd(newAsserts);
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
const handleRemoveAssert = (indexToRemove: number) => {
|
|
65
|
-
const newAsserts = asserts.filter((_, index) => index !== indexToRemove);
|
|
66
|
-
setAsserts(newAsserts);
|
|
67
|
-
onAdd(newAsserts);
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
return (
|
|
71
|
-
<>
|
|
72
|
-
<Typography variant="h6">Asserts</Typography>
|
|
73
|
-
<Box my={asserts.length > 0 ? 2 : 0}>
|
|
74
|
-
<Stack direction="column" spacing={2}>
|
|
75
|
-
{asserts.map((assert, index) => (
|
|
76
|
-
<Stack key={index} direction="row" spacing={2} alignItems="center">
|
|
77
|
-
<Autocomplete
|
|
78
|
-
value={assert.type}
|
|
79
|
-
options={assertTypes}
|
|
80
|
-
sx={{ minWidth: 200 }}
|
|
81
|
-
onChange={(event, newValue) => {
|
|
82
|
-
const newType = newValue;
|
|
83
|
-
const newAsserts = asserts.map((a, i) =>
|
|
84
|
-
i === index ? { ...a, type: newType as AssertionType } : a,
|
|
85
|
-
);
|
|
86
|
-
setAsserts(newAsserts);
|
|
87
|
-
onAdd(newAsserts);
|
|
88
|
-
}}
|
|
89
|
-
renderInput={(params) => <TextField {...params} label="Type" />}
|
|
90
|
-
/>
|
|
91
|
-
<TextField
|
|
92
|
-
label="Value"
|
|
93
|
-
value={assert.value}
|
|
94
|
-
fullWidth
|
|
95
|
-
onChange={(e) => {
|
|
96
|
-
const newValue = e.target.value;
|
|
97
|
-
const newAsserts = asserts.map((a, i) =>
|
|
98
|
-
i === index ? { ...a, value: newValue } : a,
|
|
99
|
-
);
|
|
100
|
-
setAsserts(newAsserts);
|
|
101
|
-
onAdd(newAsserts);
|
|
102
|
-
}}
|
|
103
|
-
/>
|
|
104
|
-
<IconButton onClick={() => handleRemoveAssert(index)} size="small">
|
|
105
|
-
<Delete />
|
|
106
|
-
</IconButton>
|
|
107
|
-
</Stack>
|
|
108
|
-
))}
|
|
109
|
-
</Stack>
|
|
110
|
-
</Box>
|
|
111
|
-
<Button color="primary" onClick={handleAdd}>
|
|
112
|
-
Add Assert
|
|
113
|
-
</Button>
|
|
114
|
-
</>
|
|
115
|
-
);
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
export default AssertsForm;
|