promptfoo 0.2.2 → 0.4.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 (62) hide show
  1. package/README.md +55 -17
  2. package/dist/evaluator.d.ts +1 -1
  3. package/dist/evaluator.d.ts.map +1 -1
  4. package/dist/evaluator.js +213 -150
  5. package/dist/evaluator.js.map +1 -1
  6. package/dist/main.js +51 -10
  7. package/dist/main.js.map +1 -1
  8. package/dist/prompts.d.ts +2 -0
  9. package/dist/prompts.d.ts.map +1 -0
  10. package/dist/prompts.js +21 -0
  11. package/dist/prompts.js.map +1 -0
  12. package/dist/providers.d.ts +1 -0
  13. package/dist/providers.d.ts.map +1 -1
  14. package/dist/providers.js +11 -5
  15. package/dist/providers.js.map +1 -1
  16. package/dist/tableOutput.html +5 -8
  17. package/dist/types.d.ts +27 -8
  18. package/dist/types.d.ts.map +1 -1
  19. package/dist/util.d.ts +5 -0
  20. package/dist/util.d.ts.map +1 -1
  21. package/dist/util.js +50 -1
  22. package/dist/util.js.map +1 -1
  23. package/dist/web/client/assets/index-710f1308.css +1 -0
  24. package/dist/web/client/assets/index-900b20c0.js +172 -0
  25. package/dist/web/client/favicon.ico +0 -0
  26. package/dist/web/client/index.html +15 -0
  27. package/dist/web/client/logo.svg +30 -0
  28. package/dist/web/server.d.ts +2 -0
  29. package/dist/web/server.d.ts.map +1 -0
  30. package/dist/web/server.js +74 -0
  31. package/dist/web/server.js.map +1 -0
  32. package/package.json +14 -3
  33. package/src/evaluator.ts +271 -174
  34. package/src/main.ts +52 -10
  35. package/src/prompts.ts +20 -0
  36. package/src/providers.ts +33 -15
  37. package/src/tableOutput.html +5 -8
  38. package/src/types.ts +32 -7
  39. package/src/util.ts +60 -1
  40. package/src/web/client/.eslintrc.cjs +14 -0
  41. package/src/web/client/index.html +13 -0
  42. package/src/web/client/package.json +37 -0
  43. package/src/web/client/public/favicon.ico +0 -0
  44. package/src/web/client/public/logo.svg +30 -0
  45. package/src/web/client/src/App.css +0 -0
  46. package/src/web/client/src/App.tsx +43 -0
  47. package/src/web/client/src/Logo.css +13 -0
  48. package/src/web/client/src/Logo.tsx +11 -0
  49. package/src/web/client/src/NavBar.css +3 -0
  50. package/src/web/client/src/NavBar.tsx +11 -0
  51. package/src/web/client/src/ResultsTable.css +133 -0
  52. package/src/web/client/src/ResultsTable.tsx +261 -0
  53. package/src/web/client/src/ResultsView.tsx +110 -0
  54. package/src/web/client/src/index.css +35 -0
  55. package/src/web/client/src/main.tsx +10 -0
  56. package/src/web/client/src/store.ts +13 -0
  57. package/src/web/client/src/types.ts +14 -0
  58. package/src/web/client/src/vite-env.d.ts +1 -0
  59. package/src/web/client/tsconfig.json +24 -0
  60. package/src/web/client/tsconfig.node.json +10 -0
  61. package/src/web/client/vite.config.ts +7 -0
  62. package/src/web/server.ts +96 -0
package/README.md CHANGED
@@ -13,7 +13,18 @@ With promptfoo, you can:
13
13
  - Use as a command line tool, or integrate into your workflow as a library
14
14
  - Use OpenAI API models (built-in support), or integrate custom API providers for any LLM API
15
15
 
16
- ## Usage (command line)
16
+ **» [View docs on website](https://promptfoo.dev/docs/intro) «**
17
+
18
+ promptfoo works by producing matrix views that allow you to quickly review prompt outputs across many inputs. The goal: tune prompts systematically across all relevant test cases, instead of testing prompts one-off.
19
+
20
+ Here's an example of a side-by-side comparison of multiple prompts and inputs. You can manually review outputs, or set up "expectations" that automatically flag bad outputs.
21
+
22
+ ![Prompt evaluation matrix - web viewer](https://github.com/typpo/promptfoo/assets/310310/ddcd77df-2783-425e-ade9-1a20dd0b6cd2)
23
+
24
+ It works on the command line too.
25
+ ![Prompt evaluation](https://user-images.githubusercontent.com/310310/235529431-f4d5c395-d569-448e-9697-cd637e0372a5.gif)
26
+
27
+ ## Usage (command line & web viewer)
17
28
 
18
29
  To get started, run the following command:
19
30
 
@@ -32,15 +43,22 @@ npx promptfoo eval
32
43
  If you're looking to customize your usage, you have the full set of parameters at your disposal:
33
44
 
34
45
  ```bash
35
- npx promptfoo eval -p <prompt_paths...> -o <output_path> -r <providers> [-v <vars_path>] [-j <max_concurrency] [-c <config_path>]
46
+ npx promptfoo eval -p <prompt_paths...> -o <output_path> -r <providers> [-v <vars_path>] [-j <max_concurrency] [-c <config_path>] [--grader <grading_provider>]
36
47
  ```
37
48
 
38
49
  - `<prompt_paths...>`: Paths to prompt file(s)
39
50
  - `<output_path>`: Path to output CSV, JSON, YAML, or HTML file. Defaults to terminal output
40
51
  - `<providers>`: One or more of: `openai:<model_name>`, or filesystem path to custom API caller module
41
52
  - `<vars_path>` (optional): Path to CSV, JSON, or YAML file with prompt variables
42
- - `<max_concurrency>` (optional): Number of simultaneous API requests. Defaults to 3
53
+ - `<max_concurrency>` (optional): Number of simultaneous API requests. Defaults to 4
43
54
  - `<config_path>` (optional): Path to configuration file
55
+ - `<grading_provider>`: A provider that handles the grading process, if you are using [LLM grading](#expected-outputs)
56
+
57
+ After running an eval, you may optionally use the `view` command to open the web viewer:
58
+
59
+ ```
60
+ npx promptfoo view
61
+ ```
44
62
 
45
63
  ### Examples
46
64
 
@@ -52,8 +70,6 @@ In this example, we evaluate whether adding adjectives to the personality of an
52
70
  npx promptfoo eval -p prompts.txt -v vars.csv -r openai:gpt-3.5-turbo
53
71
  ```
54
72
 
55
- ![Peek 2023-05-01 13-53](https://user-images.githubusercontent.com/310310/235529431-f4d5c395-d569-448e-9697-cd637e0372a5.gif)
56
-
57
73
  <!--
58
74
  <img width="1362" alt="Side-by-side evaluation of LLM prompt quality, terminal output" src="https://user-images.githubusercontent.com/310310/235329207-e8c22459-5f51-4fee-9714-1b602ac3d7ca.png">
59
75
 
@@ -64,7 +80,9 @@ This command will evaluate the prompts in `prompts.txt`, substituing the variabl
64
80
 
65
81
  Have a look at the setup and full output [here](https://github.com/typpo/promptfoo/tree/main/examples/assistant-cli).
66
82
 
67
- You can run the command without an `-o` option to output in your terminal ([example](https://user-images.githubusercontent.com/310310/235329207-e8c22459-5f51-4fee-9714-1b602ac3d7ca.png)), or use `-o` to specify an HTML ([example](https://user-images.githubusercontent.com/310310/235483444-4ddb832d-e103-4b9c-a862-b0d6cc11cdc0.png)), CSV ([example](https://docs.google.com/spreadsheets/d/1nanoj3_TniWrDl1Sj-qYqIMD6jwm5FBy15xPFdUTsmI/edit?usp=sharing)), JSON ([example](https://github.com/typpo/promptfoo/blob/main/examples/simple-cli/output.json)), or YAML output.
83
+ You can also output a nice [spreadsheet](https://docs.google.com/spreadsheets/d/1nanoj3_TniWrDl1Sj-qYqIMD6jwm5FBy15xPFdUTsmI/edit?usp=sharing), [JSON](https://github.com/typpo/promptfoo/blob/main/examples/simple-cli/output.json), YAML, or an HTML file:
84
+
85
+ ![Table output](https://user-images.githubusercontent.com/310310/235483444-4ddb832d-e103-4b9c-a862-b0d6cc11cdc0.png)
68
86
 
69
87
  #### Model quality
70
88
 
@@ -164,9 +182,9 @@ You can use [Nunjucks](https://mozilla.github.io/nunjucks/) templating syntax to
164
182
  Example of a single prompt file with multiple prompts (`prompts.txt`):
165
183
 
166
184
  ```
167
- Translate the following text to French: "{{text}}"
185
+ Translate the following text to French: "{{name}}: {{text}}"
168
186
  ---
169
- Translate the following text to German: "{{text}}"
187
+ Translate the following text to German: "{{name}}: {{text}}"
170
188
  ```
171
189
 
172
190
  Example of multiple prompt files:
@@ -174,13 +192,13 @@ Example of multiple prompt files:
174
192
  - `prompt1.txt`:
175
193
 
176
194
  ```
177
- Translate the following text to French: "{{text}}"
195
+ Translate the following text to French: "{{name}}: {{text}}"
178
196
  ```
179
197
 
180
198
  - `prompt2.txt`:
181
199
 
182
200
  ```
183
- Translate the following text to German: "{{text}}"
201
+ Translate the following text to German: "{{name}}: {{text}}"
184
202
  ```
185
203
 
186
204
  ### Vars File
@@ -192,24 +210,27 @@ Vars are substituted by [Nunjucks](https://mozilla.github.io/nunjucks/) templati
192
210
  Example of a vars file (`vars.csv`):
193
211
 
194
212
  ```
195
- text
196
- "Hello, world!"
197
- "Goodbye, everyone!"
213
+ "name","text"
214
+ "Bob","Hello, world!"
215
+ "Joe","Goodbye, everyone!"
198
216
  ```
199
217
 
200
218
  Example of a vars file (`vars.json`):
201
219
 
202
220
  ```json
203
- [{ "text": "Hello, world!" }, { "text": "Goodbye, everyone!" }]
221
+ [
222
+ { "name": "Bob", "text": "Hello, world!" },
223
+ { "name": "Joe", "text": "Goodbye, everyone!" }
224
+ ]
204
225
  ```
205
226
 
206
- ### Expected Value
227
+ ### Expected Outputs
207
228
 
208
- You can specify an expected value for each test case to evaluate the success or failure of the model's output. To do this, add a special field called `__expected` in the `vars` file. The `__expected` field supports three types of value comparisons:
229
+ You can specify an expected value for each test case to evaluate the success or failure of the model's output. To do this, add a special field called `__expected` in the `vars` file. The `__expected` field supports these types of value comparisons:
209
230
 
210
231
  1. If the expected value starts with `eval:`, it will evaluate the contents as the body of a JavaScript function defined like: `function(output) { <eval> }`. The function should return a boolean value, where `true` indicates success and `false` indicates failure.
211
232
 
212
- 2. If the expected value starts with `grade:`, it will call the `gradeOutput(prompt, output)` function. You should assume this function exists and returns a boolean value, where `true` indicates success and `false` indicates failure.
233
+ 2. If the expected value starts with `grade:`, it will ask an LLM to evaluate whether the output meets the condition. For example: `grade: don't mention being an AI`. This option requires a provider name to be supplied to promptfoo via the `--grader` argument: `promptfoo --grader openai:gpt-4 ...`.
213
234
 
214
235
  3. Otherwise, it attempts an exact string match comparison between the expected value and the model's output.
215
236
 
@@ -219,6 +240,7 @@ Example of a vars file with the `__expected` field (`vars.csv`):
219
240
  text,__expected
220
241
  "Hello, world!","Bonjour le monde"
221
242
  "Goodbye, everyone!","eval:return output.includes('Au revoir');"
243
+ "I am a pineapple","grade:doesn't reference any fruits besides pineapple"
222
244
  ```
223
245
 
224
246
  Example of a vars file with the `__expected` field (`vars.json`):
@@ -227,6 +249,7 @@ Example of a vars file with the `__expected` field (`vars.json`):
227
249
  [
228
250
  { "text": "Hello, world!", "__expected": "Bonjour le monde" },
229
251
  { "text": "Goodbye, everyone!", "__expected": "eval:output.includes('Au revoir');" }
252
+ { "text": "I am a pineapple", "__expected": "grade:doesn't reference any fruits besides pineapple" }
230
253
  ]
231
254
  ```
232
255
 
@@ -273,6 +296,18 @@ npm install
273
296
  npm link
274
297
  ```
275
298
 
299
+ 4. Build:
300
+
301
+ ```bash
302
+ npm run build
303
+ ```
304
+
305
+ 5. Make the entrypoint executable:
306
+
307
+ ```bash
308
+ chmod +x dist/main.js
309
+ ```
310
+
276
311
  ### Example
277
312
 
278
313
  ```bash
@@ -297,6 +332,9 @@ Other OpenAI-related environment variables are supported:
297
332
 
298
333
  - `OPENAI_TEMPERATURE` - temperature model parameter, defaults to 0
299
334
  - `OPENAI_MAX_TOKENS` - max_tokens model parameter, defaults to 1024
335
+ - `OPENAI_STOP` - stopwords in JSON format, defaults to []
336
+ - `OPENAI_API_HOST` - override the hostname for the API request. Useful for proxies like Helicone.
337
+ - `REQUEST_TIMEOUT_MS` - maximum request time, in milliseconds (defaults to 60000)
300
338
 
301
339
  The OpenAI provider supports the following model formats:
302
340
 
@@ -1,3 +1,3 @@
1
- import { EvaluateOptions, EvaluateSummary } from './types.js';
1
+ import type { EvaluateOptions, EvaluateSummary } from './types.js';
2
2
  export declare function evaluate(options: EvaluateOptions): Promise<EvaluateSummary>;
3
3
  //# sourceMappingURL=evaluator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"evaluator.d.ts","sourceRoot":"","sources":["../src/evaluator.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,eAAe,EAAE,eAAe,EAAuC,MAAM,YAAY,CAAC;AA2EnG,wBAAsB,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC,CAqIjF"}
1
+ {"version":3,"file":"evaluator.d.ts","sourceRoot":"","sources":["../src/evaluator.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAEV,eAAe,EAGf,eAAe,EAIhB,MAAM,YAAY,CAAC;AAoSpB,wBAAgB,QAAQ,CAAC,OAAO,EAAE,eAAe,4BAGhD"}
package/dist/evaluator.js CHANGED
@@ -1,175 +1,238 @@
1
1
  import async from 'async';
2
2
  import nunjucks from 'nunjucks';
3
- const DEFAULT_MAX_CONCURRENCY = 3;
4
- function checkExpectedValue(expected, output) {
5
- if (expected.startsWith('eval:')) {
6
- const evalBody = expected.slice(5);
7
- const evalFunction = new Function('output', `return ${evalBody}`);
8
- return evalFunction(output);
9
- }
10
- else if (expected.startsWith('grade:')) {
11
- // NYI
12
- return false;
3
+ import { DEFAULT_GRADING_PROMPT } from './prompts.js';
4
+ const DEFAULT_MAX_CONCURRENCY = 4;
5
+ class Evaluator {
6
+ constructor(options) {
7
+ this.options = options;
8
+ this.stats = {
9
+ successes: 0,
10
+ failures: 0,
11
+ tokenUsage: {
12
+ total: 0,
13
+ prompt: 0,
14
+ completion: 0,
15
+ },
16
+ };
13
17
  }
14
- else {
15
- return expected === output;
18
+ async gradeOutput(expected, output) {
19
+ const { grading } = this.options;
20
+ if (!grading) {
21
+ throw new Error('Cannot grade output without grading config. Specify --grader option or grading config.');
22
+ }
23
+ const prompt = nunjucks.renderString(grading.prompt || DEFAULT_GRADING_PROMPT, {
24
+ content: output,
25
+ rubric: expected,
26
+ });
27
+ const resp = await grading.provider.callApi(prompt);
28
+ if (resp.error || !resp.output) {
29
+ return {
30
+ pass: false,
31
+ reason: resp.error || 'No output',
32
+ tokensUsed: {
33
+ total: resp.tokenUsage?.total || 0,
34
+ prompt: resp.tokenUsage?.prompt || 0,
35
+ completion: resp.tokenUsage?.completion || 0,
36
+ },
37
+ };
38
+ }
39
+ try {
40
+ const parsed = JSON.parse(resp.output);
41
+ parsed.tokensUsed = {
42
+ total: resp.tokenUsage?.total || 0,
43
+ prompt: resp.tokenUsage?.prompt || 0,
44
+ completion: resp.tokenUsage?.completion || 0,
45
+ };
46
+ return parsed;
47
+ }
48
+ catch (err) {
49
+ return {
50
+ pass: false,
51
+ reason: `Output is not valid JSON: ${resp.output}`,
52
+ tokensUsed: {
53
+ total: resp.tokenUsage?.total || 0,
54
+ prompt: resp.tokenUsage?.prompt || 0,
55
+ completion: resp.tokenUsage?.completion || 0,
56
+ },
57
+ };
58
+ }
16
59
  }
17
- }
18
- async function runEval({ provider, prompt, vars, includeProviderId, }) {
19
- vars = vars || {};
20
- const renderedPrompt = nunjucks.renderString(prompt, vars);
21
- // Note that we're using original prompt, not renderedPrompt
22
- const promptDisplay = includeProviderId ? `[${provider.id()}] ${prompt}` : prompt;
23
- const setup = {
24
- prompt: {
25
- raw: renderedPrompt,
26
- display: promptDisplay,
27
- },
28
- vars,
29
- };
30
- try {
31
- const response = await provider.callApi(renderedPrompt);
32
- const ret = {
33
- ...setup,
34
- response,
35
- success: false,
36
- };
37
- if (response.error) {
38
- ret.error = response.error;
60
+ async checkExpectedValue(expected, output) {
61
+ if (expected.startsWith('eval:')) {
62
+ const evalBody = expected.slice(5);
63
+ const evalFunction = new Function('output', `return ${evalBody}`);
64
+ return { pass: evalFunction(output) };
39
65
  }
40
- else if (response.output) {
41
- const matchesExpected = vars.__expected
42
- ? checkExpectedValue(vars.__expected, response.output)
43
- : true;
44
- if (!matchesExpected) {
45
- ret.error = `Expected ${vars.__expected}, got "${response.output}"`;
46
- }
47
- ret.success = matchesExpected;
66
+ else if (expected.startsWith('grade:')) {
67
+ const gradingResult = await this.gradeOutput(expected.slice(6), output);
68
+ return {
69
+ pass: gradingResult.pass,
70
+ reason: gradingResult.reason,
71
+ };
48
72
  }
49
73
  else {
50
- ret.success = false;
51
- ret.error = 'No output';
74
+ const pass = expected === output;
75
+ return {
76
+ pass,
77
+ reason: pass ? undefined : `Expected: ${expected}, Output: ${output}`,
78
+ };
52
79
  }
53
- return ret;
54
80
  }
55
- catch (err) {
56
- return {
57
- ...setup,
58
- error: String(err),
59
- success: false,
81
+ async runEval({ provider, prompt, vars, includeProviderId, }) {
82
+ vars = vars || {};
83
+ const renderedPrompt = nunjucks.renderString(prompt, vars);
84
+ // Note that we're using original prompt, not renderedPrompt
85
+ const promptDisplay = includeProviderId ? `[${provider.id()}] ${prompt}` : prompt;
86
+ const setup = {
87
+ prompt: {
88
+ raw: renderedPrompt,
89
+ display: promptDisplay,
90
+ },
91
+ vars,
60
92
  };
61
- }
62
- }
63
- export async function evaluate(options) {
64
- const prompts = [];
65
- const results = [];
66
- for (const promptContent of options.prompts) {
67
- for (const provider of options.providers) {
68
- prompts.push({
69
- raw: promptContent,
70
- display: options.providers.length > 1 ? `[${provider.id()}] ${promptContent}` : promptContent,
71
- });
93
+ try {
94
+ const response = await provider.callApi(renderedPrompt);
95
+ const ret = {
96
+ ...setup,
97
+ response,
98
+ success: false,
99
+ };
100
+ if (response.error) {
101
+ ret.error = response.error;
102
+ }
103
+ else if (response.output) {
104
+ const checkResult = vars.__expected
105
+ ? await this.checkExpectedValue(vars.__expected, response.output)
106
+ : { pass: true };
107
+ if (!checkResult.pass) {
108
+ ret.error = checkResult.reason || `Expected: ${vars.__expected}`;
109
+ }
110
+ ret.success = checkResult.pass;
111
+ }
112
+ else {
113
+ ret.success = false;
114
+ ret.error = 'No output';
115
+ }
116
+ // Update token usage stats
117
+ this.stats.tokenUsage.total += response.tokenUsage?.total || 0;
118
+ this.stats.tokenUsage.prompt += response.tokenUsage?.prompt || 0;
119
+ this.stats.tokenUsage.completion += response.tokenUsage?.completion || 0;
120
+ if (ret.success) {
121
+ this.stats.successes++;
122
+ }
123
+ else {
124
+ this.stats.failures++;
125
+ }
126
+ return ret;
127
+ }
128
+ catch (err) {
129
+ return {
130
+ ...setup,
131
+ error: String(err),
132
+ success: false,
133
+ };
72
134
  }
73
135
  }
74
- const vars = options.vars && options.vars.length > 0 ? options.vars : [{}];
75
- const varsWithExpectedKeyRemoved = vars.map((v) => {
76
- const ret = { ...v };
77
- delete ret.__expected;
78
- return ret;
79
- });
80
- const isTest = vars[0].__expected;
81
- const table = [
82
- isTest
83
- ? [
84
- 'RESULT',
85
- [...prompts.map((p) => p.display), ...Object.keys(varsWithExpectedKeyRemoved[0])],
86
- ].flat()
87
- : [...prompts.map((p) => p.display), ...Object.keys(varsWithExpectedKeyRemoved[0])],
88
- ];
89
- const stats = {
90
- successes: 0,
91
- failures: 0,
92
- tokenUsage: {
93
- total: 0,
94
- prompt: 0,
95
- completion: 0,
96
- },
97
- };
98
- let progressbar;
99
- if (options.showProgressBar) {
100
- const totalNumRuns = options.prompts.length * options.providers.length * (options.vars?.length || 1);
101
- const cliProgress = await import('cli-progress');
102
- progressbar = new cliProgress.SingleBar({
103
- format: 'Eval: [{bar}] {percentage}% | ETA: {eta}s | {value}/{total} | {provider} "{prompt}" {vars}',
104
- }, cliProgress.Presets.shades_classic);
105
- progressbar.start(totalNumRuns, 0, {
106
- provider: '',
107
- prompt: '',
108
- vars: '',
109
- });
110
- }
111
- const runEvalOptions = [];
112
- for (const row of vars) {
136
+ async evaluate() {
137
+ const options = this.options;
138
+ const prompts = [];
113
139
  for (const promptContent of options.prompts) {
114
140
  for (const provider of options.providers) {
115
- runEvalOptions.push({
116
- provider,
117
- prompt: promptContent,
118
- vars: row,
119
- includeProviderId: options.providers.length > 1,
141
+ const display = options.providers.length > 1 ? `[${provider.id()}] ${promptContent}` : promptContent;
142
+ prompts.push({
143
+ raw: promptContent,
144
+ display,
120
145
  });
121
146
  }
122
147
  }
123
- }
124
- const combinedOutputs = new Array(vars.length).fill(null).map(() => []);
125
- await async.forEachOfLimit(runEvalOptions, options.maxConcurrency || DEFAULT_MAX_CONCURRENCY, async (options, index) => {
126
- const row = await runEval(options);
127
- results[index] = row;
128
- if (row.error) {
129
- stats.failures++;
148
+ const vars = options.vars && options.vars.length > 0 ? options.vars : [{}];
149
+ const varsWithExpectedKeyRemoved = vars.map((v) => {
150
+ const ret = { ...v };
151
+ delete ret.__expected;
152
+ return ret;
153
+ });
154
+ const isTest = vars[0].__expected;
155
+ const table = {
156
+ head: {
157
+ prompts: prompts.map((p) => p.display),
158
+ vars: Object.keys(varsWithExpectedKeyRemoved[0]),
159
+ },
160
+ body: [],
161
+ };
162
+ let progressbar;
163
+ if (options.showProgressBar) {
164
+ const totalNumRuns = options.prompts.length * options.providers.length * (options.vars?.length || 1);
165
+ const cliProgress = await import('cli-progress');
166
+ progressbar = new cliProgress.SingleBar({
167
+ format: 'Eval: [{bar}] {percentage}% | ETA: {eta}s | {value}/{total} | {provider} "{prompt}" {vars}',
168
+ }, cliProgress.Presets.shades_classic);
169
+ progressbar.start(totalNumRuns, 0, {
170
+ provider: '',
171
+ prompt: '',
172
+ vars: '',
173
+ });
130
174
  }
131
- else {
132
- if (row.success) {
133
- stats.successes++;
134
- }
135
- else {
136
- stats.failures++;
175
+ const runEvalOptions = [];
176
+ let rowIndex = 0;
177
+ for (const row of vars) {
178
+ let colIndex = 0;
179
+ for (const promptContent of options.prompts) {
180
+ for (const provider of options.providers) {
181
+ runEvalOptions.push({
182
+ provider,
183
+ prompt: promptContent,
184
+ vars: row,
185
+ includeProviderId: options.providers.length > 1,
186
+ rowIndex,
187
+ colIndex,
188
+ });
189
+ colIndex++;
190
+ }
137
191
  }
138
- stats.tokenUsage.total += row.response?.tokenUsage?.total || 0;
139
- stats.tokenUsage.prompt += row.response?.tokenUsage?.prompt || 0;
140
- stats.tokenUsage.completion += row.response?.tokenUsage?.completion || 0;
192
+ rowIndex++;
141
193
  }
194
+ const results = [];
195
+ await async.forEachOfLimit(runEvalOptions, options.maxConcurrency || DEFAULT_MAX_CONCURRENCY, async (options, index) => {
196
+ const row = await this.runEval(options);
197
+ results.push(row);
198
+ if (progressbar) {
199
+ progressbar.increment({
200
+ provider: options.provider.id(),
201
+ prompt: options.prompt.slice(0, 10),
202
+ vars: Object.entries(options.vars || {})
203
+ .map(([k, v]) => `${k}=${v}`)
204
+ .join(' ')
205
+ .slice(0, 10),
206
+ });
207
+ }
208
+ // Bookkeeping for table
209
+ if (typeof index !== 'number') {
210
+ throw new Error('Expected index to be a number');
211
+ }
212
+ const resultText = isTest
213
+ ? row.success
214
+ ? `[PASS] ${row.response?.output || row.error || ''}`
215
+ : `[FAIL] ${row.error}\n---\n${row.response?.output || row.error || ''}`
216
+ : row.response?.output || row.error || '';
217
+ // TODO(ian): Provide full context in table cells, and have the caller
218
+ // construct the table contents itself.
219
+ const { rowIndex, colIndex } = options;
220
+ if (!table.body[rowIndex]) {
221
+ table.body[rowIndex] = {
222
+ outputs: [],
223
+ vars: Object.values(options.vars || {}),
224
+ };
225
+ }
226
+ table.body[rowIndex].outputs[colIndex] = resultText;
227
+ });
142
228
  if (progressbar) {
143
- progressbar.increment({
144
- provider: options.provider.id(),
145
- prompt: options.prompt.slice(0, 10),
146
- vars: Object.entries(options.vars || {})
147
- .map(([k, v]) => `${k}=${v}`)
148
- .join(' ')
149
- .slice(0, 10),
150
- });
151
- }
152
- // Bookkeeping for table
153
- if (typeof index !== 'number') {
154
- throw new Error('Expected index to be a number');
229
+ progressbar.stop();
155
230
  }
156
- const combinedOutputIndex = Math.floor(index / prompts.length);
157
- combinedOutputs[combinedOutputIndex].push(row.response?.output || row.error || '');
158
- });
159
- if (progressbar) {
160
- progressbar.stop();
161
- }
162
- // TODO(ian): Display errors in table UI.
163
- if (isTest) {
164
- table.push(...combinedOutputs.map((output, index) => [
165
- results[index].success ? 'PASS' : `FAIL: ${results[index].error}`,
166
- ...output,
167
- ...Object.values(varsWithExpectedKeyRemoved[index]),
168
- ]));
231
+ return { version: 1, results, stats: this.stats, table };
169
232
  }
170
- else {
171
- table.push(...combinedOutputs.map((output, index) => [...output, ...Object.values(vars[index])]));
172
- }
173
- return { results, stats, table };
233
+ }
234
+ export function evaluate(options) {
235
+ const ev = new Evaluator(options);
236
+ return ev.evaluate();
174
237
  }
175
238
  //# sourceMappingURL=evaluator.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"evaluator.js","sourceRoot":"","sources":["../src/evaluator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"evaluator.js","sourceRoot":"","sources":["../src/evaluator.ts"],"names":[],"mappings":"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"}