evals 1.0.2 → 1.0.3

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 (3) hide show
  1. package/dist/index.js +1147 -1425
  2. package/package.json +1 -1
  3. package/src/index.js +181 -9
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
7
- "version": "1.0.2",
7
+ "version": "1.0.3",
8
8
  "description": "Umbrage Evals CLI",
9
9
  "bin": {
10
10
  "evals": "./src/index.js"
package/src/index.js CHANGED
@@ -1,16 +1,188 @@
1
1
  #!/usr/bin/env node
2
- const { program } = require('commander');
3
- const fetchEvals = require('./fetchEvals');
4
- const runEvals = require('./runEvals');
2
+ import { program } from 'commander';
3
+ import fs from 'fs';
4
+ import OpenAI from 'openai';
5
5
 
6
6
  program
7
- .command('fetch-evals')
8
- .description('Fetch the latest evals for the project')
9
- .action(fetchEvals);
7
+ .command('fetch-evals')
8
+ .description('Fetch the latest evals for the project')
9
+ .action(async () => {
10
+ if (!process.env.UMBRAGE_EVALS_API_KEY) {
11
+ throw new Error('UMBRAGE_EVALS_API_KEY is not set in the environment variables.');
12
+ }
13
+
14
+ const UMBRAGE_EVALS_API_KEY = process.env.UMBRAGE_EVALS_API_KEY;
15
+
16
+ const fetchEvals = async () => {
17
+ const url = new URL('https://api-gateway.groff.workers.dev/evals');
18
+
19
+ url.searchParams.append('page', 0);
20
+ url.searchParams.append('pageSize', 100);
21
+ url.searchParams.append('eval_type', 'OpenAI-GPT-4');
22
+
23
+ try {
24
+ const response = await fetch(url, {
25
+ method: 'GET',
26
+ headers: {
27
+ Authorization: `Bearer ${UMBRAGE_EVALS_API_KEY}`,
28
+ },
29
+ });
30
+
31
+ if (!response.ok) {
32
+ throw new Error(`HTTP error! status: ${response.status}`);
33
+ }
34
+
35
+ const data = await response.json();
36
+ return data.evals;
37
+ } catch (error) {
38
+ console.error('Error fetching evals:', error);
39
+ return []; // Return an empty array to avoid breaking downstream code
40
+ }
41
+ };
42
+
43
+ const processPromptFile = async file => {
44
+ const promptFilename = file.split('.prompt.js')[0];
45
+ const evalsFolder = `${promptFilename}_evals`;
46
+
47
+ if (!fs.existsSync(evalsFolder)) {
48
+ fs.mkdirSync(evalsFolder, { recursive: true });
49
+ }
50
+
51
+ // Fetch evals from the cloudflare worker
52
+ const evalsForPrompt = await fetchEvals();
53
+
54
+ for (const evalObject of evalsForPrompt) {
55
+ const { name: evalName, eval_code } = evalObject;
56
+ const markdownFileName = `${evalsFolder}/${evalName.replace(/[^a-z0-9]/gi, '_')}.md`;
57
+ fs.writeFileSync(markdownFileName, eval_code);
58
+ }
59
+ };
60
+
61
+ try {
62
+ const promptsDir = './prompts/';
63
+ const promptFiles = fs.readdirSync(promptsDir).filter(file => file.endsWith('.prompt.js'));
64
+
65
+ const processingPromises = promptFiles.map(processPromptFile);
66
+ await Promise.all(processingPromises);
67
+
68
+ console.log('Done fetching evals!');
69
+ } catch (error) {
70
+ console.error('An error occurred:', error);
71
+ }
72
+ });
10
73
 
11
74
  program
12
- .command('run-evals')
13
- .description('Run evals in the current directory and log results')
14
- .action(runEvals);
75
+ .command('run-evals')
76
+ .description('Run evals in the current directory and log results')
77
+ .action(async () => {
78
+ if (!process.env.OPENAI_API_KEY) {
79
+ throw new Error('OPENAI_API_KEY is not set in the environment variables.');
80
+ }
81
+
82
+ const openai = new OpenAI(); // Assumes OPENAI_API_KEY is set in environment
83
+ const promptsDir = './prompts/';
84
+ const model = 'gpt-4-1106-preview';
85
+ const temperature = 0;
86
+
87
+ // Process a single markdown file
88
+ const processMarkdownFile = async (evalsFolder, evalFile, promptInstance) => {
89
+ console.log(`\nEvaluating: ${evalFile}`);
90
+ const evalName = evalFile.split('.md')[0];
91
+ const eval_code = fs.readFileSync(`${evalsFolder}/${evalFile}`, 'utf-8');
92
+
93
+ console.time('Model response time');
94
+ const { response: modelResponse, prompts: evalPrompts } = await promptInstance.callModel('Hi! What is your name?');
95
+ console.timeEnd('Model response time');
96
+
97
+ const messages = [
98
+ {
99
+ role: 'system',
100
+ content:
101
+ 'You are a prompt evaluation expert. \\nYou will respond in JSON format with an "explanation" of why you have given it a grade from 0-100, and "suggestions" for improving the response in order to get a higher grade, and lastly the "grade" from 0-100 in integer number format. \\nUse the rubric to accomplish this task.',
102
+ },
103
+ { role: 'function', name: 'grading_rubric', content: eval_code },
104
+ { role: 'user', content: `Grade the following response using the rubric: \\n ${modelResponse}` },
105
+ ];
106
+
107
+ console.time('Eval response time');
108
+ const evalResponse = await openai.chat.completions.create({
109
+ model,
110
+ messages,
111
+ temperature,
112
+ response_format: { type: 'json_object' },
113
+ });
114
+ console.timeEnd('Eval response time');
115
+
116
+ const evalResult = JSON.parse(evalResponse.choices[0].message.content);
117
+ console.log('evalResult', evalResult);
118
+
119
+ return {
120
+ evalName,
121
+ evalCode: eval_code,
122
+ evalResult,
123
+ isValid: evalResult.grade && evalResult.explanation && evalResult.suggestions,
124
+ };
125
+ };
126
+
127
+ // Process a single prompt file
128
+ const processPromptFile = async file => {
129
+ const promptFilename = file.split('.prompt.js')[0];
130
+ const evalsFolder = `${promptsDir}/${promptFilename}_evals`;
131
+
132
+ if (!fs.existsSync(evalsFolder)) {
133
+ console.error(`Evals folder not found for ${promptFilename}, please run fetch_latest_evals.js first.`);
134
+ return;
135
+ }
136
+
137
+ const promptInstance = await import(`${promptsDir}${file}`).then(mod => mod.default);
138
+ const evaluations = [];
139
+
140
+ const evalMarkdownFiles = fs.readdirSync(evalsFolder).filter(f => f.endsWith('.md'));
141
+
142
+ for (const evalFile of evalMarkdownFiles) {
143
+ const result = await processMarkdownFile(evalsFolder, evalFile, promptInstance);
144
+ if (result.isValid) {
145
+ evaluations.push({
146
+ promptName: promptInstance.promptName,
147
+ modelName: promptInstance.modelName,
148
+ modelSettings: promptInstance.modelSettings,
149
+ modelResponse: result.modelResponse,
150
+ evalPromptsJson: JSON.stringify(result.evalPrompts),
151
+ evalName: result.evalName,
152
+ evalCode: result.evalCode,
153
+ grade: result.evalResult.grade,
154
+ explanation: result.evalResult.explanation,
155
+ suggestions: result.evalResult.suggestions,
156
+ });
157
+ } else {
158
+ // Handle invalid evaluation
159
+ evaluations.push({
160
+ promptName: promptInstance.promptName,
161
+ modelName: promptInstance.modelName,
162
+ modelSettings: promptInstance.modelSettings,
163
+ modelResponse: result.modelResponse,
164
+ evalPromptsJson: JSON.stringify(result.evalPrompts),
165
+ evalName: result.evalName,
166
+ evalCode: result.evalCode,
167
+ grade: 'Evaluation failed.',
168
+ explanation: 'Evaluation failed.',
169
+ suggestions: 'Evaluation failed.',
170
+ });
171
+ }
172
+ }
173
+
174
+ const jsonFilePath = `${evalsFolder}/${promptFilename}_evals_results_${new Date().toISOString()}.json`;
175
+ fs.writeFileSync(jsonFilePath, JSON.stringify(evaluations, null, 4));
176
+ };
177
+
178
+ try {
179
+ const promptFiles = fs.readdirSync(promptsDir).filter(file => file.endsWith('.prompt.js'));
180
+ const processingPromises = promptFiles.map(processPromptFile);
181
+ await Promise.all(processingPromises);
182
+ console.log('Done processing evals!');
183
+ } catch (error) {
184
+ console.error('An error occurred:', error);
185
+ }
186
+ });
15
187
 
16
188
  program.parse(process.argv);