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.
Files changed (184) hide show
  1. package/README.md +1 -1
  2. package/dist/package.json +4 -4
  3. package/dist/src/assertions.d.ts.map +1 -1
  4. package/dist/src/assertions.js +5 -0
  5. package/dist/src/assertions.js.map +1 -1
  6. package/dist/src/evaluator.js +1 -1
  7. package/dist/src/evaluator.js.map +1 -1
  8. package/dist/src/index.d.ts +1 -5
  9. package/dist/src/index.d.ts.map +1 -1
  10. package/dist/src/index.js +1 -1
  11. package/dist/src/index.js.map +1 -1
  12. package/dist/src/matchers.d.ts +3 -2
  13. package/dist/src/matchers.d.ts.map +1 -1
  14. package/dist/src/matchers.js +37 -9
  15. package/dist/src/matchers.js.map +1 -1
  16. package/dist/src/providers/anthropic.d.ts +5 -3
  17. package/dist/src/providers/anthropic.d.ts.map +1 -1
  18. package/dist/src/providers/anthropic.js +8 -10
  19. package/dist/src/providers/anthropic.js.map +1 -1
  20. package/dist/src/providers/azureopenai.d.ts +9 -8
  21. package/dist/src/providers/azureopenai.d.ts.map +1 -1
  22. package/dist/src/providers/azureopenai.js +33 -36
  23. package/dist/src/providers/azureopenai.js.map +1 -1
  24. package/dist/src/providers/openai.d.ts +12 -12
  25. package/dist/src/providers/openai.d.ts.map +1 -1
  26. package/dist/src/providers/openai.js +54 -65
  27. package/dist/src/providers/openai.js.map +1 -1
  28. package/dist/src/providers/replicate.d.ts +4 -2
  29. package/dist/src/providers/replicate.d.ts.map +1 -1
  30. package/dist/src/providers/replicate.js +10 -8
  31. package/dist/src/providers/replicate.js.map +1 -1
  32. package/dist/src/providers/webhook.d.ts +9 -0
  33. package/dist/src/providers/webhook.d.ts.map +1 -0
  34. package/dist/src/providers/webhook.js +54 -0
  35. package/dist/src/providers/webhook.js.map +1 -0
  36. package/dist/src/providers.d.ts +1 -1
  37. package/dist/src/providers.d.ts.map +1 -1
  38. package/dist/src/providers.js +36 -28
  39. package/dist/src/providers.js.map +1 -1
  40. package/dist/src/suggestions.d.ts.map +1 -1
  41. package/dist/src/suggestions.js +1 -3
  42. package/dist/src/suggestions.js.map +1 -1
  43. package/dist/src/types.d.ts +7 -1
  44. package/dist/src/types.d.ts.map +1 -1
  45. package/dist/src/util.js +1 -1
  46. package/dist/src/util.js.map +1 -1
  47. package/dist/src/web/nextui/404/index.html +1 -1
  48. package/dist/src/web/nextui/404.html +1 -1
  49. package/dist/src/web/nextui/_next/static/Bl3o5lF4ON7Fjki46lPhr/_buildManifest.js +1 -0
  50. package/dist/src/web/nextui/_next/static/chunks/226-7bbb6c98a19542fd.js +37 -0
  51. package/dist/src/web/nextui/_next/static/chunks/249-ea9c0f034888ccff.js +125 -0
  52. package/dist/src/web/nextui/_next/static/chunks/339-501c32916b785ef1.js +1 -0
  53. package/dist/src/web/nextui/_next/static/chunks/365-e426ea5bc7e815fc.js +8 -0
  54. package/dist/src/web/nextui/_next/static/chunks/396-0a51429a01e24cdd.js +1 -0
  55. package/dist/src/web/nextui/_next/static/chunks/596-297f7ff4a0436e87.js +25 -0
  56. package/dist/src/web/nextui/_next/static/chunks/613-572c22424de64659.js +1 -0
  57. package/dist/src/web/nextui/_next/static/chunks/706-ae1d3352d28419e9.js +9 -0
  58. package/dist/src/web/nextui/_next/static/chunks/891-7035926a62c1c4e0.js +1 -0
  59. package/dist/src/web/nextui/_next/static/chunks/app/eval/[id]/not-found-366629541fd598e9.js +1 -0
  60. package/dist/src/web/nextui/_next/static/chunks/app/eval/[id]/page-319d2ee38d37574e.js +1 -0
  61. package/dist/src/web/nextui/_next/static/chunks/app/eval/page-a6b1ff91723b7beb.js +1 -0
  62. package/dist/src/web/nextui/_next/static/chunks/app/layout-024c4adc71c9feb0.js +1 -0
  63. package/dist/src/web/nextui/_next/static/chunks/app/page-1ae60660130041b2.js +1 -0
  64. package/dist/src/web/nextui/_next/static/chunks/app/setup/page-6ef16148040bf4f4.js +1 -0
  65. package/dist/src/web/nextui/_next/static/chunks/{ca377847-cb6ae6a6a073aebb.js → ca377847-26b462611379a4f7.js} +3 -3
  66. package/dist/src/web/nextui/_next/static/chunks/{fd9d1056-ac777be631f5a9e9.js → fd9d1056-fba4b53a2f01213b.js} +1 -1
  67. package/dist/src/web/nextui/_next/static/chunks/framework-8883d1e9be70c3da.js +25 -0
  68. package/dist/src/web/nextui/_next/static/chunks/main-8ea85465d428ecfe.js +1 -0
  69. package/dist/src/web/nextui/_next/static/chunks/main-app-581ccf0003955b21.js +1 -0
  70. package/dist/src/web/nextui/_next/static/chunks/pages/_app-52924524f99094ab.js +1 -0
  71. package/dist/src/web/nextui/_next/static/chunks/pages/_error-c92d5c4bb2b49926.js +1 -0
  72. package/dist/src/web/nextui/_next/static/chunks/webpack-55c264ce2fd85eb7.js +1 -0
  73. package/dist/src/web/nextui/_next/static/css/4d399fceacd06992.css +1 -0
  74. package/dist/src/web/nextui/eval/index.html +1 -1
  75. package/dist/src/web/nextui/eval/index.txt +6 -6
  76. package/dist/src/web/nextui/index.html +1 -1
  77. package/dist/src/web/nextui/index.txt +5 -5
  78. package/dist/src/web/nextui/setup/index.html +27 -1
  79. package/dist/src/web/nextui/setup/index.txt +9 -9
  80. package/dist/src/web/server.d.ts.map +1 -1
  81. package/dist/src/web/server.js +9 -5
  82. package/dist/src/web/server.js.map +1 -1
  83. package/package.json +4 -4
  84. package/dist/src/web/nextui/_next/static/US6gOx8LHTX_Hzm9aYNrC/_buildManifest.js +0 -1
  85. package/dist/src/web/nextui/_next/static/chunks/339-4fc8a80fa840e771.js +0 -1
  86. package/dist/src/web/nextui/_next/static/chunks/373-8a280796c0f2d1af.js +0 -1
  87. package/dist/src/web/nextui/_next/static/chunks/583-125d32af505e9bc4.js +0 -1
  88. package/dist/src/web/nextui/_next/static/chunks/596-07e4a23a5c6cdf04.js +0 -25
  89. package/dist/src/web/nextui/_next/static/chunks/658-a62210d07dc4dcb6.js +0 -15
  90. package/dist/src/web/nextui/_next/static/chunks/707-699cbd84b259c37b.js +0 -37
  91. package/dist/src/web/nextui/_next/static/chunks/858-ceb6fa22e614492b.js +0 -125
  92. package/dist/src/web/nextui/_next/static/chunks/891-3000ea7c0a292558.js +0 -1
  93. package/dist/src/web/nextui/_next/static/chunks/app/eval/[id]/not-found-50e40614fa05600e.js +0 -1
  94. package/dist/src/web/nextui/_next/static/chunks/app/eval/[id]/page-c19c44ed1b2dfb58.js +0 -1
  95. package/dist/src/web/nextui/_next/static/chunks/app/eval/page-d4a1813b2f8c4532.js +0 -1
  96. package/dist/src/web/nextui/_next/static/chunks/app/layout-664a8d716d2d24b1.js +0 -1
  97. package/dist/src/web/nextui/_next/static/chunks/app/page-1f8ef6a00a2355f0.js +0 -1
  98. package/dist/src/web/nextui/_next/static/chunks/app/setup/page-182018a3c6397345.js +0 -1
  99. package/dist/src/web/nextui/_next/static/chunks/framework-43665103d101a22d.js +0 -25
  100. package/dist/src/web/nextui/_next/static/chunks/main-50cc0a98559591ce.js +0 -1
  101. package/dist/src/web/nextui/_next/static/chunks/main-app-c9dc13756d166550.js +0 -1
  102. package/dist/src/web/nextui/_next/static/chunks/pages/_app-6b79a29ad0d63b21.js +0 -1
  103. package/dist/src/web/nextui/_next/static/chunks/pages/_error-9aeb3e4d490fe4b8.js +0 -1
  104. package/dist/src/web/nextui/_next/static/chunks/webpack-6e474e42be502dd7.js +0 -1
  105. package/dist/src/web/nextui/_next/static/css/a35c840ac696f161.css +0 -1
  106. package/dist/src/web/nextui/api +0 -1
  107. package/src/__mocks__/esm.ts +0 -3
  108. package/src/assertions.ts +0 -580
  109. package/src/cache.ts +0 -109
  110. package/src/esm.ts +0 -13
  111. package/src/evaluator.ts +0 -500
  112. package/src/index.ts +0 -52
  113. package/src/logger.ts +0 -46
  114. package/src/main.ts +0 -442
  115. package/src/matchers.ts +0 -120
  116. package/src/onboarding.ts +0 -69
  117. package/src/prompts.ts +0 -39
  118. package/src/providers/anthropic.ts +0 -88
  119. package/src/providers/azureopenai.ts +0 -299
  120. package/src/providers/llama.ts +0 -95
  121. package/src/providers/localai.ts +0 -111
  122. package/src/providers/ollama.ts +0 -89
  123. package/src/providers/openai.ts +0 -337
  124. package/src/providers/replicate.ts +0 -99
  125. package/src/providers/scriptCompletion.ts +0 -35
  126. package/src/providers/shared.ts +0 -34
  127. package/src/providers.ts +0 -192
  128. package/src/share.ts +0 -27
  129. package/src/suggestions.ts +0 -63
  130. package/src/table.ts +0 -43
  131. package/src/tableOutput.html +0 -52
  132. package/src/telemetry.ts +0 -70
  133. package/src/types.ts +0 -299
  134. package/src/updates.ts +0 -46
  135. package/src/util.ts +0 -543
  136. package/src/web/nextui/.eslintrc.json +0 -3
  137. package/src/web/nextui/next.config.js +0 -14
  138. package/src/web/nextui/package-lock.json +0 -4644
  139. package/src/web/nextui/package.json +0 -47
  140. package/src/web/nextui/public/favicon.ico +0 -0
  141. package/src/web/nextui/public/logo.svg +0 -30
  142. package/src/web/nextui/src/app/Home.css +0 -3
  143. package/src/web/nextui/src/app/api/route.ts +0 -6
  144. package/src/web/nextui/src/app/components/DarkMode.css +0 -22
  145. package/src/web/nextui/src/app/components/DarkMode.tsx +0 -17
  146. package/src/web/nextui/src/app/components/Logo.css +0 -32
  147. package/src/web/nextui/src/app/components/Logo.tsx +0 -11
  148. package/src/web/nextui/src/app/components/PageShell.css +0 -33
  149. package/src/web/nextui/src/app/components/PageShell.tsx +0 -87
  150. package/src/web/nextui/src/app/eval/ConfigModal.tsx +0 -84
  151. package/src/web/nextui/src/app/eval/Eval.css +0 -13
  152. package/src/web/nextui/src/app/eval/Eval.tsx +0 -79
  153. package/src/web/nextui/src/app/eval/EvalOutputPromptDialog.tsx +0 -127
  154. package/src/web/nextui/src/app/eval/ResultsCharts.tsx +0 -355
  155. package/src/web/nextui/src/app/eval/ResultsTable.css +0 -179
  156. package/src/web/nextui/src/app/eval/ResultsTable.tsx +0 -503
  157. package/src/web/nextui/src/app/eval/ResultsView.tsx +0 -301
  158. package/src/web/nextui/src/app/eval/ShareModal.tsx +0 -70
  159. package/src/web/nextui/src/app/eval/[id]/not-found.tsx +0 -5
  160. package/src/web/nextui/src/app/eval/[id]/page.css +0 -9
  161. package/src/web/nextui/src/app/eval/[id]/page.tsx +0 -20
  162. package/src/web/nextui/src/app/eval/index.css +0 -0
  163. package/src/web/nextui/src/app/eval/page.tsx +0 -8
  164. package/src/web/nextui/src/app/eval/store.ts +0 -18
  165. package/src/web/nextui/src/app/eval/types.ts +0 -20
  166. package/src/web/nextui/src/app/globals.css +0 -58
  167. package/src/web/nextui/src/app/layout.tsx +0 -25
  168. package/src/web/nextui/src/app/page.tsx +0 -7
  169. package/src/web/nextui/src/app/setup/AssertsForm.tsx +0 -118
  170. package/src/web/nextui/src/app/setup/PromptDialog.tsx +0 -77
  171. package/src/web/nextui/src/app/setup/PromptsSection.tsx +0 -190
  172. package/src/web/nextui/src/app/setup/ProviderConfigDialog.tsx +0 -99
  173. package/src/web/nextui/src/app/setup/ProviderSelector.tsx +0 -149
  174. package/src/web/nextui/src/app/setup/RunTestSuiteButton.tsx +0 -88
  175. package/src/web/nextui/src/app/setup/TestCaseDialog.tsx +0 -108
  176. package/src/web/nextui/src/app/setup/TestCasesSection.tsx +0 -154
  177. package/src/web/nextui/src/app/setup/VarsForm.tsx +0 -57
  178. package/src/web/nextui/src/app/setup/page.css +0 -3
  179. package/src/web/nextui/src/app/setup/page.tsx +0 -160
  180. package/src/web/nextui/src/util/api.ts +0 -1
  181. package/src/web/nextui/src/util/store.ts +0 -53
  182. package/src/web/nextui/tsconfig.json +0 -28
  183. package/src/web/server.ts +0 -151
  184. /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,5 +0,0 @@
1
- import './page.css';
2
-
3
- export default function NotFound() {
4
- return <div className="error">Eval not found</div>;
5
- }
@@ -1,9 +0,0 @@
1
- .error {
2
- display: flex;
3
- flex-direction: column;
4
- gap: 1.5rem;
5
- justify-content: center;
6
- align-items: center;
7
- height: 9rem;
8
- font-size: 1.5rem;
9
- }
@@ -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,8 +0,0 @@
1
- import React from 'react';
2
-
3
- import Eval from './Eval';
4
-
5
- export default function Page() {
6
- // Just return local eval from websocket
7
- return <Eval />;
8
- }
@@ -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,7 +0,0 @@
1
- import { redirect } from 'next/navigation';
2
-
3
- import './Home.css';
4
-
5
- export default function Page() {
6
- redirect('/eval');
7
- }
@@ -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;