promptfoo 0.17.6 → 0.17.8
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/dist/package.json +2 -2
- package/dist/src/assertions.js +2 -2
- package/dist/src/assertions.js.map +1 -1
- package/dist/src/evaluator.d.ts.map +1 -1
- package/dist/src/evaluator.js +37 -6
- package/dist/src/evaluator.js.map +1 -1
- package/dist/src/main.js +4 -0
- package/dist/src/main.js.map +1 -1
- package/dist/src/providers/azureopenai.d.ts +4 -0
- package/dist/src/providers/azureopenai.d.ts.map +1 -1
- package/dist/src/providers/azureopenai.js +15 -0
- package/dist/src/providers/azureopenai.js.map +1 -1
- package/dist/src/providers/openai.d.ts +5 -0
- package/dist/src/providers/openai.d.ts.map +1 -1
- package/dist/src/providers/openai.js +21 -2
- package/dist/src/providers/openai.js.map +1 -1
- package/dist/src/providers/replicate.d.ts +8 -1
- package/dist/src/providers/replicate.d.ts.map +1 -1
- package/dist/src/providers/replicate.js +9 -6
- package/dist/src/providers/replicate.js.map +1 -1
- package/dist/src/providers/shared.d.ts.map +1 -1
- package/dist/src/providers/shared.js.map +1 -1
- package/dist/src/providers.js +1 -1
- package/dist/src/providers.js.map +1 -1
- package/dist/src/types.d.ts +6 -1
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/util.d.ts +8 -1
- package/dist/src/util.d.ts.map +1 -1
- package/dist/src/util.js +81 -26
- package/dist/src/util.js.map +1 -1
- package/dist/src/web/client/assets/{index-13198388.js → index-0c6f887d.js} +25 -25
- package/dist/src/web/client/index.html +1 -1
- package/dist/src/web/server.d.ts.map +1 -1
- package/dist/src/web/server.js +26 -3
- package/dist/src/web/server.js.map +1 -1
- package/package.json +2 -2
- package/src/assertions.ts +2 -2
- package/src/evaluator.ts +42 -6
- package/src/main.ts +6 -0
- package/src/providers/azureopenai.ts +24 -0
- package/src/providers/openai.ts +33 -3
- package/src/providers/replicate.ts +20 -7
- package/src/providers/shared.ts +3 -1
- package/src/providers.ts +1 -1
- package/src/types.ts +10 -1
- package/src/util.ts +95 -27
- package/src/web/client/src/App.tsx +24 -1
- package/src/web/client/src/ResultsView.tsx +42 -3
- package/src/web/server.ts +33 -10
- package/src/web/client/package-lock.json +0 -5726
|
@@ -14,6 +14,7 @@ function App() {
|
|
|
14
14
|
const { table, setTable, setConfig } = useStore();
|
|
15
15
|
const [loaded, setLoaded] = React.useState<boolean>(false);
|
|
16
16
|
const loadedFromApi = React.useRef(false);
|
|
17
|
+
const [recentFiles, setRecentFiles] = React.useState<string[]>([]);
|
|
17
18
|
|
|
18
19
|
const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
|
|
19
20
|
const [darkMode, setDarkMode] = React.useState(prefersDarkMode);
|
|
@@ -43,6 +44,22 @@ function App() {
|
|
|
43
44
|
}
|
|
44
45
|
}, [prefersDarkMode]);
|
|
45
46
|
|
|
47
|
+
const fetchRecentFiles = async () => {
|
|
48
|
+
if (!window.location.href.includes('localhost')) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const resp = await fetch(`http://localhost:15500/results`);
|
|
52
|
+
const body = await resp.json();
|
|
53
|
+
setRecentFiles(body.data);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const handleRecentFileSelection = async (file: string) => {
|
|
57
|
+
const resp = await fetch(`http://localhost:15500/results/${file}`);
|
|
58
|
+
const body = await resp.json();
|
|
59
|
+
setTable(body.data.results.table);
|
|
60
|
+
setConfig(body.data.config);
|
|
61
|
+
};
|
|
62
|
+
|
|
46
63
|
React.useEffect(() => {
|
|
47
64
|
const fetchEvalData = async (id: string) => {
|
|
48
65
|
if (loadedFromApi.current) {
|
|
@@ -72,12 +89,14 @@ function App() {
|
|
|
72
89
|
setLoaded(true);
|
|
73
90
|
setTable(data.results.table);
|
|
74
91
|
setConfig(data.config);
|
|
92
|
+
fetchRecentFiles();
|
|
75
93
|
});
|
|
76
94
|
|
|
77
95
|
socket.on('update', (data) => {
|
|
78
96
|
console.log('Received data update', data);
|
|
79
97
|
setTable(data.results.table);
|
|
80
98
|
setConfig(data.config);
|
|
99
|
+
fetchRecentFiles();
|
|
81
100
|
});
|
|
82
101
|
}
|
|
83
102
|
|
|
@@ -89,7 +108,11 @@ function App() {
|
|
|
89
108
|
return (
|
|
90
109
|
<ThemeProvider theme={theme}>
|
|
91
110
|
<NavBar darkMode={darkMode} onToggleDarkMode={toggleDarkMode} />
|
|
92
|
-
{loaded && table ?
|
|
111
|
+
{loaded && table ? (
|
|
112
|
+
<ResultsView recentFiles={recentFiles} onRecentFileSelected={handleRecentFileSelection} />
|
|
113
|
+
) : (
|
|
114
|
+
<div>Loading...</div>
|
|
115
|
+
)}
|
|
93
116
|
</ThemeProvider>
|
|
94
117
|
);
|
|
95
118
|
}
|
|
@@ -37,7 +37,26 @@ const ResponsiveStack = styled(Stack)(({ theme }) => ({
|
|
|
37
37
|
},
|
|
38
38
|
}));
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
function filenameToDate(filename: string) {
|
|
41
|
+
const dateString = filename.slice('eval-'.length, filename.length - '.json'.length);
|
|
42
|
+
const date = new Date(dateString);
|
|
43
|
+
return date.toLocaleDateString('en-US', {
|
|
44
|
+
year: 'numeric',
|
|
45
|
+
month: 'long',
|
|
46
|
+
day: 'numeric',
|
|
47
|
+
hour: '2-digit',
|
|
48
|
+
minute: '2-digit',
|
|
49
|
+
second: '2-digit',
|
|
50
|
+
timeZoneName: 'short',
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
interface ResultsViewProps {
|
|
55
|
+
recentFiles: string[];
|
|
56
|
+
onRecentFileSelected: (file: string) => void;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export default function ResultsView({ recentFiles, onRecentFileSelected }: ResultsViewProps) {
|
|
41
60
|
const { table, config } = useStore();
|
|
42
61
|
const [maxTextLength, setMaxTextLength] = React.useState(250);
|
|
43
62
|
const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({});
|
|
@@ -148,10 +167,30 @@ export default function ResultsView() {
|
|
|
148
167
|
return (
|
|
149
168
|
<div>
|
|
150
169
|
<Paper py="md">
|
|
151
|
-
<ResponsiveStack direction="row" spacing={
|
|
170
|
+
<ResponsiveStack direction="row" spacing={4} alignItems="center">
|
|
171
|
+
<Box>
|
|
172
|
+
{recentFiles && recentFiles.length > 0 && (
|
|
173
|
+
<FormControl sx={{ m: 1, minWidth: 200 }} size="small">
|
|
174
|
+
<InputLabel>View run</InputLabel>
|
|
175
|
+
<Select
|
|
176
|
+
key={recentFiles.join(',')}
|
|
177
|
+
className="recent-files"
|
|
178
|
+
label="Previous runs"
|
|
179
|
+
defaultValue={recentFiles[0]}
|
|
180
|
+
onChange={(e: SelectChangeEvent) => onRecentFileSelected(e.target.value)}
|
|
181
|
+
>
|
|
182
|
+
{recentFiles.map((file) => (
|
|
183
|
+
<MenuItem key={file} value={file}>
|
|
184
|
+
{filenameToDate(file)}
|
|
185
|
+
</MenuItem>
|
|
186
|
+
))}
|
|
187
|
+
</Select>
|
|
188
|
+
</FormControl>
|
|
189
|
+
)}
|
|
190
|
+
</Box>
|
|
152
191
|
<Box>
|
|
153
192
|
<FormControl sx={{ m: 1, minWidth: 200 }} size="small">
|
|
154
|
-
<InputLabel id="visible-columns-label">
|
|
193
|
+
<InputLabel id="visible-columns-label">Show columns</InputLabel>
|
|
155
194
|
<Select
|
|
156
195
|
labelId="visible-columns-label"
|
|
157
196
|
id="visible-columns"
|
package/src/web/server.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
1
|
+
import fs, { Stats } from 'fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import readline from 'node:readline';
|
|
4
4
|
import http from 'node:http';
|
|
@@ -11,7 +11,7 @@ import { Server as SocketIOServer } from 'socket.io';
|
|
|
11
11
|
|
|
12
12
|
import logger from '../logger';
|
|
13
13
|
import { getDirectory } from '../esm';
|
|
14
|
-
import { getLatestResultsPath } from '../util';
|
|
14
|
+
import { getLatestResultsPath, listPreviousResults, readResult } from '../util';
|
|
15
15
|
|
|
16
16
|
export function init(port = 15500) {
|
|
17
17
|
const app = express();
|
|
@@ -40,14 +40,37 @@ export function init(port = 15500) {
|
|
|
40
40
|
socket.emit('init', readLatestJson());
|
|
41
41
|
|
|
42
42
|
// Watch for changes to latest.json and emit the update event
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
43
|
+
const watcher = debounce((curr: Stats, prev: Stats) => {
|
|
44
|
+
if (curr.mtime !== prev.mtime) {
|
|
45
|
+
socket.emit('update', readLatestJson());
|
|
46
|
+
}
|
|
47
|
+
}, 250);
|
|
48
|
+
fs.watchFile(latestJsonPath, watcher);
|
|
49
|
+
|
|
50
|
+
// Stop watching the file when the socket connection is closed
|
|
51
|
+
socket.on('disconnect', () => {
|
|
52
|
+
fs.unwatchFile(latestJsonPath, watcher);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
app.get('/results', (req, res) => {
|
|
57
|
+
const previousResults = listPreviousResults();
|
|
58
|
+
res.json({ data: previousResults });
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
app.get('/results/:filename', (req, res) => {
|
|
62
|
+
const filename = req.params.filename;
|
|
63
|
+
const safeFilename = path.basename(filename);
|
|
64
|
+
if (safeFilename !== filename || !listPreviousResults().includes(safeFilename)) {
|
|
65
|
+
res.status(400).send('Invalid filename');
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const result = readResult(safeFilename);
|
|
69
|
+
if (!result) {
|
|
70
|
+
res.status(404).send('Result not found');
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
res.json({ data: result });
|
|
51
74
|
});
|
|
52
75
|
|
|
53
76
|
httpServer.listen(port, () => {
|