From c31dfab62e914109ca98d56bd1282b9642c35e0a Mon Sep 17 00:00:00 2001 From: Vince Loewe Date: Mon, 1 Jul 2024 21:11:04 +0100 Subject: [PATCH] fix: invalid CSV export --- package-lock.json | 29 ++++++++++++++ .../backend/src/api/v1/evaluations/utils.ts | 6 +-- .../components/evals/ResultsMatrix.tsx | 40 +++++++++---------- packages/frontend/package.json | 3 +- 4 files changed, 52 insertions(+), 26 deletions(-) diff --git a/package-lock.json b/package-lock.json index dcf34878..c053207c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2990,6 +2990,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/deeks": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/deeks/-/deeks-3.1.0.tgz", + "integrity": "sha512-e7oWH1LzIdv/prMQ7pmlDlaVoL64glqzvNgkgQNgyec9ORPHrT2jaOqMtRyqJuwWjtfb6v+2rk9pmaHj+F137A==", + "engines": { + "node": ">= 16" + } + }, "node_modules/deep-equal": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", @@ -3074,6 +3082,14 @@ "node": ">=8" } }, + "node_modules/doc-path": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/doc-path/-/doc-path-4.1.1.tgz", + "integrity": "sha512-h1ErTglQAVv2gCnOpD3sFS6uolDbOKHDU1BZq+Kl3npPqroU3dYL42lUgMfd5UimlwtRgp7C9dLGwqQ5D2HYgQ==", + "engines": { + "node": ">=16" + } + }, "node_modules/dom-helpers": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", @@ -4077,6 +4093,18 @@ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, + "node_modules/json-2-csv": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/json-2-csv/-/json-2-csv-5.5.1.tgz", + "integrity": "sha512-KgAtAXTQopRwe90gh8SgjRSxgt9bUWbGAPMo9W0TZLA8SqiQH7khtagFfeEUjG3NBPwJu/+9uX5pMvunKaPvrQ==", + "dependencies": { + "deeks": "3.1.0", + "doc-path": "4.1.1" + }, + "engines": { + "node": ">= 16" + } + }, "node_modules/json-stable-stringify": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.1.1.tgz", @@ -7349,6 +7377,7 @@ "date-fns": "^3.6.0", "dayjs": "^1.11.11", "jose": "^5.2.0", + "json-2-csv": "^5.5.1", "jsonrepair": "^3.5.1", "next": "^14.1.0", "next-seo": "^6.4.0", diff --git a/packages/backend/src/api/v1/evaluations/utils.ts b/packages/backend/src/api/v1/evaluations/utils.ts index b8a11ad8..5a254b3f 100644 --- a/packages/backend/src/api/v1/evaluations/utils.ts +++ b/packages/backend/src/api/v1/evaluations/utils.ts @@ -68,10 +68,10 @@ export async function runEval({ const endedAt = new Date() // Create virtual run to be able to run checks - const output = res.choices[0].message + const output = res?.choices[0].message - const promptTokens = res.usage?.prompt_tokens - const completionTokens = res.usage?.completion_tokens + const promptTokens = res?.usage?.prompt_tokens + const completionTokens = res?.usage?.completion_tokens const duration = endedAt.getTime() - createdAt.getTime() const virtualRun = { diff --git a/packages/frontend/components/evals/ResultsMatrix.tsx b/packages/frontend/components/evals/ResultsMatrix.tsx index 60b97027..9adc6ea5 100644 --- a/packages/frontend/components/evals/ResultsMatrix.tsx +++ b/packages/frontend/components/evals/ResultsMatrix.tsx @@ -15,6 +15,8 @@ import SmartViewer from "../SmartViewer" import { MODELS, Provider } from "shared" import { IconFileExport } from "@tabler/icons-react" +import { json2csv } from "json-2-csv" + // We create a matrix of results for each prompt, variable and model. // The matrix is a 3D array, where each dimension represents a different variable, prompt and model. @@ -178,15 +180,8 @@ export default function ResultsMatrix({ data, showTestIndicator }) { ...prompts.map((messages) => getVariableKeysForPrompt(messages).length), ) - function exportToCsv() { - const columns = [ - "Prompt", - "Variable Variation", - "Model", - "Passed", - "Output", - ] - const rows = [] + async function exportToCsv() { + const rows = [] as any[] prompts.forEach((messages) => { const variableVariations = getVariableVariationsForPrompt(messages) @@ -202,26 +197,27 @@ export default function ResultsMatrix({ data, showTestIndicator }) { ? JSON.stringify(result.error) : result.output?.content - rows.push([ - JSON.stringify(messages), - JSON.stringify(variables), - provider.model, - result.passed ? "Yes" : "No", - `"${textResult.replace(/"/g, '""')}"`, // Escape double quotes and wrap in double quotes - ]) + rows.push( + { + Prompt: JSON.stringify(messages), + Variables: JSON.stringify(variables), + Model: provider.model, + Passed: result.passed ? "Yes" : "No", + Output: textResult, + }, // Escape double quotes and wrap in double quotes + ) } }) }) }) - const csvContent = [ - columns.join(","), - ...rows.map((row) => row.join(",")), - ].join("\n") + const csv = await json2csv(rows, { + arrayIndexesAsKeys: false, + }) - const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" }) - const link = document.createElement("a") + const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" }) const url = URL.createObjectURL(blob) + const link = document.createElement("a") link.setAttribute("href", url) link.setAttribute("download", "results.csv") link.style.visibility = "hidden" diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 9f6a2fba..d196642e 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -8,13 +8,13 @@ "load-env": "bash ./load-env.sh" }, "dependencies": { + "@mantine/charts": "7.10.0", "@mantine/core": "7.10.0", "@mantine/dates": "^7.10.0", "@mantine/form": "7.10.0", "@mantine/hooks": "7.10.0", "@mantine/modals": "7.10.0", "@mantine/notifications": "7.10.0", - "@mantine/charts": "7.10.0", "@sentry/nextjs": "^7.109.0", "@tabler/icons-react": "^2.46.0", "@tanstack/react-table": "^8.11.2", @@ -24,6 +24,7 @@ "date-fns": "^3.6.0", "dayjs": "^1.11.11", "jose": "^5.2.0", + "json-2-csv": "^5.5.1", "jsonrepair": "^3.5.1", "next": "^14.1.0", "next-seo": "^6.4.0",