From 8c9fe90860aad3ba32dbf1b682e87c0a41a40302 Mon Sep 17 00:00:00 2001 From: Matt Pocock Date: Sun, 19 Oct 2025 12:44:47 +0100 Subject: [PATCH 001/106] Changed default storage to in-memory. SQLite still available via config. --- .changeset/0000-export-command-change.md | 5 ++ .changeset/0000-in-memory-default.md | 5 ++ .../evalite-tests/tests/export-static.test.ts | 59 +++++++++++++++++++ .../evalite/src/backend-only-constants.ts | 1 - packages/evalite/src/command.ts | 43 ++++++++++---- packages/evalite/src/run-evalite.ts | 5 +- 6 files changed, 104 insertions(+), 14 deletions(-) create mode 100644 .changeset/0000-export-command-change.md create mode 100644 .changeset/0000-in-memory-default.md diff --git a/.changeset/0000-export-command-change.md b/.changeset/0000-export-command-change.md new file mode 100644 index 00000000..12562b8b --- /dev/null +++ b/.changeset/0000-export-command-change.md @@ -0,0 +1,5 @@ +--- +"evalite": major +--- + +Export command now uses the storage specified in the config and auto-runs if empty. diff --git a/.changeset/0000-in-memory-default.md b/.changeset/0000-in-memory-default.md new file mode 100644 index 00000000..d5bb9e51 --- /dev/null +++ b/.changeset/0000-in-memory-default.md @@ -0,0 +1,5 @@ +--- +"evalite": major +--- + +Changed default storage to in-memory. SQLite still available via config. diff --git a/packages/evalite-tests/tests/export-static.test.ts b/packages/evalite-tests/tests/export-static.test.ts index 14d1aeae..4882da58 100644 --- a/packages/evalite-tests/tests/export-static.test.ts +++ b/packages/evalite-tests/tests/export-static.test.ts @@ -3,6 +3,9 @@ import { readdir, readFile } from "node:fs/promises"; import path from "node:path"; import { expect, it } from "vitest"; import { getEvalsAsRecordViaStorage, loadFixture } from "./test-utils.js"; +import { createInMemoryStorage } from "evalite/in-memory-storage"; +import { createSqliteStorage } from "evalite/sqlite-storage"; +import { runEvalite } from "evalite/runner"; it("Should export all required files and directory structure", async () => { await using fixture = await loadFixture("export"); @@ -258,3 +261,59 @@ it("Should rewrite /assets/ paths in JS files with custom basePath", async () => expect(jsFilesWithAssetReferences).toBeGreaterThan(0); }); + +it("Should error when runId specified but run not found", async () => { + const storage = createInMemoryStorage(); + const exportDir = "./test-export"; + + await expect( + exportStaticUI({ + storage, + outputPath: exportDir, + runId: 999, + }) + ).rejects.toThrow("Run with ID 999 not found"); + + await storage.close(); +}); + +it("Should error when no runs found in storage", async () => { + const storage = createInMemoryStorage(); + const exportDir = "./test-export"; + + // Empty storage should error when trying to export + await expect( + exportStaticUI({ + storage, + outputPath: exportDir, + }) + ).rejects.toThrow("No runs found"); + + await storage.close(); +}); + +it("Should export existing data from SQLite storage", async () => { + await using fixture = await loadFixture("export"); + + // Create SQLite storage and run evals with it + const dbPath = path.join( + fixture.dir, + "node_modules", + ".evalite", + "cache.sqlite" + ); + await using sqliteStorage = await createSqliteStorage(dbPath); + + await runEvalite({ + cwd: fixture.dir, + mode: "run-once-and-exit", + storage: sqliteStorage, + }); + + const exportDir = path.join(fixture.dir, "sqlite-export"); + + await exportStaticUI({ + storage: sqliteStorage, + outputPath: exportDir, + }); +}); diff --git a/packages/evalite/src/backend-only-constants.ts b/packages/evalite/src/backend-only-constants.ts index ff672b47..42cf303f 100644 --- a/packages/evalite/src/backend-only-constants.ts +++ b/packages/evalite/src/backend-only-constants.ts @@ -3,5 +3,4 @@ import path from "node:path"; import { CACHE_LOCATION } from "./constants.js"; -export const DB_LOCATION = path.join(CACHE_LOCATION, "cache.sqlite"); export const FILES_LOCATION = path.join(CACHE_LOCATION, "files"); diff --git a/packages/evalite/src/command.ts b/packages/evalite/src/command.ts index 9cdf8d44..22461805 100644 --- a/packages/evalite/src/command.ts +++ b/packages/evalite/src/command.ts @@ -7,8 +7,9 @@ import { import { createRequire } from "node:module"; import { exportStaticUI } from "./export-static.js"; import { createSqliteStorage } from "./storage/sqlite.js"; +import { createInMemoryStorage } from "./storage/in-memory.js"; import path from "node:path"; -import { DB_LOCATION } from "./backend-only-constants.js"; +import { loadEvaliteConfig } from "./config.js"; const packageJson = createRequire(import.meta.url)( "../package.json" @@ -216,18 +217,40 @@ export const program = createProgram({ }, export: async (opts) => { const cwd = process.cwd(); - const dbPath = path.join(cwd, DB_LOCATION); - const storage = await createSqliteStorage(dbPath); - try { - await exportStaticUI({ + // Load config and determine storage (same logic as run-evalite.ts) + const config = await loadEvaliteConfig(cwd); + await using storage = config?.storage + ? await config.storage() + : createInMemoryStorage(); + + // Check if storage has any runs + const existingRuns = await storage.runs.getMany({ limit: 1 }); + const isEmpty = existingRuns.length === 0; + + // Error if runId specified but storage is empty + if (opts.runId && isEmpty) { + throw new Error( + "Cannot export with runId when storage is empty. Run evaluations first or omit runId to auto-run." + ); + } + + // Auto-run if storage is empty + if (isEmpty) { + console.log("Storage is empty. Running evaluations first..."); + await runEvalite({ + path: undefined, + cwd, + mode: "run-once-and-exit", storage, - outputPath: opts.output ?? "./evalite-export", - runId: opts.runId, - basePath: opts.basePath, }); - } finally { - await storage.close(); } + + await exportStaticUI({ + storage, + outputPath: opts.output ?? "./evalite-export", + runId: opts.runId, + basePath: opts.basePath, + }); }, }); diff --git a/packages/evalite/src/run-evalite.ts b/packages/evalite/src/run-evalite.ts index 5b66b4b7..9be07a3b 100644 --- a/packages/evalite/src/run-evalite.ts +++ b/packages/evalite/src/run-evalite.ts @@ -4,7 +4,7 @@ import { Writable } from "stream"; import { createVitest, registerConsoleShortcuts } from "vitest/node"; import { createInMemoryStorage } from "./storage/in-memory.js"; import { computeAverageScores } from "./storage/utils.js"; -import { DB_LOCATION, FILES_LOCATION } from "./backend-only-constants.js"; +import { FILES_LOCATION } from "./backend-only-constants.js"; import { DEFAULT_SERVER_PORT } from "./constants.js"; import EvaliteReporter from "./reporter.js"; import { createServer } from "./server.js"; @@ -218,8 +218,7 @@ export const runEvalite = async (opts: { } if (!storage) { - const dbLocation = path.join(cwd, DB_LOCATION); - storage = await createSqliteStorage(dbLocation); + storage = createInMemoryStorage(); } const scoreThreshold = opts.scoreThreshold ?? config?.scoreThreshold; From 15cfacea4f8e8920173704c4e1023d9742ccdbfe Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Sun, 19 Oct 2025 11:57:27 +0000 Subject: [PATCH 002/106] Remove problematic backend-only-constants imports - Remove unused DB_LOCATION import from test-utils.ts - Replace FILES_LOCATION import with local constant in files.test.ts Co-authored-by: Matt Pocock --- packages/evalite-tests/tests/files.test.ts | 3 ++- packages/evalite-tests/tests/test-utils.ts | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/evalite-tests/tests/files.test.ts b/packages/evalite-tests/tests/files.test.ts index d166ec59..e8389b36 100644 --- a/packages/evalite-tests/tests/files.test.ts +++ b/packages/evalite-tests/tests/files.test.ts @@ -1,10 +1,11 @@ import { EvaliteFile } from "evalite"; -import { FILES_LOCATION } from "evalite/backend-only-constants"; import { readdir, readFile } from "node:fs/promises"; import path from "node:path"; import { expect, it } from "vitest"; import { getEvalsAsRecordViaStorage, loadFixture } from "./test-utils.js"; +const FILES_LOCATION = "./node_modules/.evalite/files"; + it("Should save files returned from task() in node_modules", async () => { await using fixture = await loadFixture("files"); diff --git a/packages/evalite-tests/tests/test-utils.ts b/packages/evalite-tests/tests/test-utils.ts index 16deb1ea..909429a4 100644 --- a/packages/evalite-tests/tests/test-utils.ts +++ b/packages/evalite-tests/tests/test-utils.ts @@ -1,4 +1,3 @@ -import { DB_LOCATION } from "evalite/backend-only-constants"; import { randomUUID } from "crypto"; import { cpSync, rmSync } from "fs"; import path from "path"; From ab2af0db9b87d496ee32c50512fb67aca23e9a29 Mon Sep 17 00:00:00 2001 From: Matt Pocock Date: Sun, 19 Oct 2025 13:06:22 +0100 Subject: [PATCH 003/106] Fixed CI properly --- packages/evalite-tests/tests/files.test.ts | 3 +-- packages/evalite/src/backend-only-constants.ts | 3 ++- packages/evalite/src/constants.ts | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/evalite-tests/tests/files.test.ts b/packages/evalite-tests/tests/files.test.ts index e8389b36..80620af4 100644 --- a/packages/evalite-tests/tests/files.test.ts +++ b/packages/evalite-tests/tests/files.test.ts @@ -3,8 +3,7 @@ import { readdir, readFile } from "node:fs/promises"; import path from "node:path"; import { expect, it } from "vitest"; import { getEvalsAsRecordViaStorage, loadFixture } from "./test-utils.js"; - -const FILES_LOCATION = "./node_modules/.evalite/files"; +import { FILES_LOCATION } from "evalite/backend-only-constants"; it("Should save files returned from task() in node_modules", async () => { await using fixture = await loadFixture("files"); diff --git a/packages/evalite/src/backend-only-constants.ts b/packages/evalite/src/backend-only-constants.ts index 42cf303f..97710424 100644 --- a/packages/evalite/src/backend-only-constants.ts +++ b/packages/evalite/src/backend-only-constants.ts @@ -1,6 +1,7 @@ // Constants that cannot be used in frontend code import path from "node:path"; -import { CACHE_LOCATION } from "./constants.js"; + +const CACHE_LOCATION = path.join("node_modules", ".evalite"); export const FILES_LOCATION = path.join(CACHE_LOCATION, "files"); diff --git a/packages/evalite/src/constants.ts b/packages/evalite/src/constants.ts index dd088ccd..58429752 100644 --- a/packages/evalite/src/constants.ts +++ b/packages/evalite/src/constants.ts @@ -1,2 +1 @@ export const DEFAULT_SERVER_PORT = 3006; -export const CACHE_LOCATION = "./node_modules/.evalite"; From ed18fcbc828d12e6dc2f7f823ef607117757a292 Mon Sep 17 00:00:00 2001 From: Matt Pocock Date: Sun, 19 Oct 2025 17:15:51 +0100 Subject: [PATCH 004/106] Huge move from evals -> suites, and results -> evals --- apps/evalite-ui/app/data/queries.ts | 20 +- .../app/hooks/use-server-state-utils.ts | 6 +- apps/evalite-ui/app/routes/$.tsx | 6 +- apps/evalite-ui/app/routes/__root.tsx | 61 ++- ...ex.tsx => suite.$name.eval.$evalIndex.tsx} | 105 +++-- .../{eval.$name.tsx => suite.$name.tsx} | 202 +++++---- apps/evalite-ui/app/sdk.ts | 38 +- .../evalite-tests/tests/ai-sdk-traces.test.ts | 12 +- packages/evalite-tests/tests/basics.test.ts | 10 +- packages/evalite-tests/tests/columns.test.ts | 6 +- packages/evalite-tests/tests/config.test.ts | 10 +- .../evalite-tests/tests/custom-scorer.test.ts | 8 +- .../evalite-tests/tests/export-static.test.ts | 35 +- packages/evalite-tests/tests/failing.test.ts | 8 +- packages/evalite-tests/tests/files.test.ts | 22 +- .../evalite-tests/tests/much-data.test.ts | 6 +- .../evalite-tests/tests/no-scorers.test.ts | 10 +- .../tests/non-serializable-data.test.ts | 12 +- packages/evalite-tests/tests/objects.test.ts | 6 +- .../evalite-tests/tests/only-flag.test.ts | 30 +- .../evalite-tests/tests/output-path.test.ts | 85 ++-- packages/evalite-tests/tests/paths.test.ts | 6 +- .../tests/polymorphic-data.test.ts | 8 +- packages/evalite-tests/tests/stream.test.ts | 6 +- packages/evalite-tests/tests/test-utils.ts | 55 +-- packages/evalite-tests/tests/timeout.test.ts | 6 +- packages/evalite-tests/tests/traces.test.ts | 8 +- .../evalite-tests/tests/trial-count.test.ts | 24 +- packages/evalite-tests/tests/variants.test.ts | 14 +- packages/evalite/src/ai-sdk.ts | 3 +- packages/evalite/src/evalite.ts | 24 +- packages/evalite/src/export-static.ts | 177 ++++---- packages/evalite/src/reporter.ts | 44 +- .../evalite/src/reporter/EvaliteRunner.ts | 172 ++++---- packages/evalite/src/reporter/events.ts | 16 +- packages/evalite/src/reporter/rendering.ts | 38 +- packages/evalite/src/run-evalite.ts | 80 ++-- packages/evalite/src/sdk.ts | 52 +-- packages/evalite/src/server.ts | 158 ++++--- packages/evalite/src/storage/in-memory.ts | 136 +++--- packages/evalite/src/storage/sqlite.ts | 238 ++++++----- packages/evalite/src/storage/storage.test.ts | 392 +++++++++--------- packages/evalite/src/storage/utils.ts | 12 +- packages/evalite/src/types.ts | 183 ++++---- 44 files changed, 1271 insertions(+), 1279 deletions(-) rename apps/evalite-ui/app/routes/{eval.$name.result.$resultIndex.tsx => suite.$name.eval.$evalIndex.tsx} (83%) rename apps/evalite-ui/app/routes/{eval.$name.tsx => suite.$name.tsx} (73%) diff --git a/apps/evalite-ui/app/data/queries.ts b/apps/evalite-ui/app/data/queries.ts index 5462af7e..53eb5cc8 100644 --- a/apps/evalite-ui/app/data/queries.ts +++ b/apps/evalite-ui/app/data/queries.ts @@ -1,5 +1,5 @@ -import { getMenuItems, getServerState, getResult, getEvalByName } from "~/sdk"; import { queryOptions } from "@tanstack/react-query"; +import { getEval, getMenuItems, getServerState, getSuiteByName } from "~/sdk"; export const getMenuItemsQueryOptions = queryOptions({ queryKey: ["menu-items"] as const, @@ -11,21 +11,21 @@ export const getServerStateQueryOptions = queryOptions({ queryFn: getServerState, }); -export const getEvalByNameQueryOptions = ( +export const getSuiteByNameQueryOptions = ( name: string, timestamp: string | null | undefined ) => queryOptions({ - queryKey: ["eval-by-name", name, timestamp] as const, - queryFn: () => getEvalByName(name, timestamp), + queryKey: ["suite-by-name", name, timestamp] as const, + queryFn: () => getSuiteByName(name, timestamp), }); -export const getResultQueryOptions = (opts: { - evalName: string; - evalTimestamp: string | null | undefined; - resultIndex: string; +export const getEvalQueryOptions = (opts: { + suiteName: string; + suiteTimestamp: string | null | undefined; + evalIndex: string; }) => queryOptions({ - queryKey: ["result", opts] as const, - queryFn: () => getResult(opts), + queryKey: ["eval", opts] as const, + queryFn: () => getEval(opts), }); diff --git a/apps/evalite-ui/app/hooks/use-server-state-utils.ts b/apps/evalite-ui/app/hooks/use-server-state-utils.ts index 7524bcd1..3b39e2e9 100644 --- a/apps/evalite-ui/app/hooks/use-server-state-utils.ts +++ b/apps/evalite-ui/app/hooks/use-server-state-utils.ts @@ -9,12 +9,12 @@ export const useServerStateUtils = (state: Evalite.ServerState) => { const isRunningFilepath = (filepath: string) => filePathSet.has(filepath) && state.type === "running"; - const isRunningEvalName = (name: string) => - state.type === "running" && state.evalNamesRunning.includes(name); + const isRunningSuiteName = (name: string) => + state.type === "running" && state.suiteNamesRunning.includes(name); return { isRunningFilepath, - isRunningEvalName, + isRunningSuiteName, }; }, [state]); }; diff --git a/apps/evalite-ui/app/routes/$.tsx b/apps/evalite-ui/app/routes/$.tsx index a2e5ed76..76883e8f 100644 --- a/apps/evalite-ui/app/routes/$.tsx +++ b/apps/evalite-ui/app/routes/$.tsx @@ -5,15 +5,15 @@ export const Route = createFileRoute("/$")({ component: IndexRoute, loader: async ({ context }) => { const { queryClient } = context; - const { evals: currentEvals } = await queryClient.ensureQueryData( + const { suites: currentSuites } = await queryClient.ensureQueryData( getMenuItemsQueryOptions ); - const firstName = currentEvals[0]?.name; + const firstName = currentSuites[0]?.name; if (firstName) { return redirect({ - to: "/eval/$name", + to: "/suite/$name", params: { name: firstName, }, diff --git a/apps/evalite-ui/app/routes/__root.tsx b/apps/evalite-ui/app/routes/__root.tsx index 49772b36..28e4b228 100644 --- a/apps/evalite-ui/app/routes/__root.tsx +++ b/apps/evalite-ui/app/routes/__root.tsx @@ -44,51 +44,51 @@ const TanStackRouterDevtools = })) ); -type EvalWithState = Evalite.SDK.GetMenuItemsResultEval & { +type SuiteWithState = Evalite.SDK.GetMenuItemsResultSuite & { state: ScoreState; }; -type GroupedEval = - | { type: "single"; eval: EvalWithState } +type GroupedSuite = + | { type: "single"; suite: SuiteWithState } | { type: "group"; groupName: string; - variants: EvalWithState[]; + variants: SuiteWithState[]; }; const getMenuItemsWithSelect = queryOptions({ ...getMenuItemsQueryOptions, select: (data) => { - const { evals: currentEvals, prevScore, score, evalStatus } = data; + const { suites: currentSuites, prevScore, score, runStatus } = data; // Add state to evals - const evalsWithState: EvalWithState[] = currentEvals.map((e) => ({ + const suitesWithState: SuiteWithState[] = currentSuites.map((e) => ({ ...e, state: getScoreState({ - status: e.evalStatus, + status: e.suiteStatus, score: e.score, prevScore: e.prevScore, }), })); - const hasScores = currentEvals.some((e) => e.hasScores); + const hasScores = currentSuites.some((e) => e.hasScores); // Group by variantGroup - const grouped: GroupedEval[] = []; - const variantGroups = new Map(); + const grouped: GroupedSuite[] = []; + const variantGroups = new Map(); - for (const evalItem of evalsWithState) { - if (evalItem.variantGroup) { + for (const suite of suitesWithState) { + if (suite.variantGroup) { // This is a variant eval - const existing = variantGroups.get(evalItem.variantGroup); + const existing = variantGroups.get(suite.variantGroup); if (existing) { - existing.push(evalItem); + existing.push(suite); } else { - variantGroups.set(evalItem.variantGroup, [evalItem]); + variantGroups.set(suite.variantGroup, [suite]); } } else { // Regular eval - grouped.push({ type: "single", eval: evalItem }); + grouped.push({ type: "single", suite: suite }); } } @@ -102,7 +102,7 @@ const getMenuItemsWithSelect = queryOptions({ groupedEvals: grouped, score, prevScore, - evalStatus, + runStatus, hasScores, }; }, @@ -123,11 +123,10 @@ export const Route = createRootRouteWithContext<{ export default function App() { const [ { - data: { groupedEvals, score, prevScore, evalStatus, hasScores }, + data: { groupedEvals, score, prevScore, hasScores, runStatus }, }, - { data: serverState }, ] = useSuspenseQueries({ - queries: [getMenuItemsWithSelect, getServerStateQueryOptions], + queries: [getMenuItemsWithSelect], }); const queryClient = useQueryClient(); @@ -158,7 +157,7 @@ export default function App() { state={getScoreState({ score, prevScore, - status: evalStatus, + status: runStatus, })} iconClassName="size-4" hasScores={hasScores} @@ -173,12 +172,12 @@ export default function App() { if (item.type === "single") { return ( ); } else { @@ -204,7 +203,7 @@ export default function App() { const VariantGroup = (props: { groupName: string; - variants: EvalWithState[]; + variants: SuiteWithState[]; }) => { return ( <> @@ -221,7 +220,7 @@ const VariantGroup = (props: { variantName={variant.variantName} score={variant.score} state={variant.state} - evalStatus={variant.evalStatus} + suiteStatus={variant.suiteStatus} isVariant={true} hasScores={variant.hasScores} /> @@ -235,7 +234,7 @@ const EvalSidebarItem = (props: { variantName?: string | undefined; state: ScoreState; score: number; - evalStatus: Evalite.Storage.Entities.EvalStatus; + suiteStatus: Evalite.Storage.Entities.SuiteStatus; isVariant?: boolean; hasScores: boolean; }) => { @@ -243,7 +242,7 @@ const EvalSidebarItem = (props: { ({ timestamp: search.timestamp, @@ -41,10 +41,10 @@ export const Route = createFileRoute("/eval/$name/result/$resultIndex")({ await Promise.all([ queryClient.ensureQueryData( - getResultQueryOptions({ - evalName: params.name!, - resultIndex: params.resultIndex!, - evalTimestamp: deps.timestamp ?? null, + getEvalQueryOptions({ + suiteName: params.name!, + evalIndex: params.evalIndex!, + suiteTimestamp: deps.timestamp ?? null, }) ), queryClient.ensureQueryData(getServerStateQueryOptions), @@ -85,19 +85,19 @@ const MainBodySection = ({ ); function ResultComponent() { - const { name, resultIndex } = Route.useParams(); + const { name, evalIndex } = Route.useParams(); const { timestamp, trace: traceIndex } = Route.useSearch(); const [ { - data: { result, prevResult, evaluation }, + data: { eval: _eval, prevEval, suite }, }, { data: serverState }, ] = useSuspenseQueries({ queries: [ - getResultQueryOptions({ - evalName: name!, - resultIndex: resultIndex!, - evalTimestamp: timestamp ?? null, + getEvalQueryOptions({ + suiteName: name!, + evalIndex: evalIndex!, + suiteTimestamp: timestamp ?? null, }), getServerStateQueryOptions, ], @@ -105,60 +105,56 @@ function ResultComponent() { const serverStateUtils = useServerStateUtils(serverState); const isRunning = - serverStateUtils.isRunningEvalName(name) && - evaluation.created_at === timestamp; + serverStateUtils.isRunningSuiteName(name) && suite.created_at === timestamp; - const startTime = result.traces[0]?.start_time ?? 0; - const endTime = result.traces[result.traces.length - 1]?.end_time ?? 0; + const startTime = _eval.traces[0]?.start_time ?? 0; + const endTime = _eval.traces[_eval.traces.length - 1]?.end_time ?? 0; const totalTraceDuration = endTime - startTime; - const traceBeingViewed = - traceIndex != null ? result.traces[traceIndex] : null; + const traceBeingViewed = traceIndex != null ? _eval.traces[traceIndex] : null; const wholeEvalUsage = - result.traces.length > 0 && - result.traces.every( + _eval.traces.length > 0 && + _eval.traces.every( (t) => typeof t.input_tokens === "number" && typeof t.output_tokens === "number" && typeof t.total_tokens === "number" ) ? { - input_tokens: sum(result.traces, (t) => t.input_tokens), - output_tokens: sum(result.traces, (t) => t.output_tokens), - total_tokens: sum(result.traces, (t) => t.total_tokens), + input_tokens: sum(_eval.traces, (t) => t.input_tokens), + output_tokens: sum(_eval.traces, (t) => t.output_tokens), + total_tokens: sum(_eval.traces, (t) => t.total_tokens), } : undefined; - const hasCustomColumns = isArrayOfRenderedColumns(result.rendered_columns); + const hasCustomColumns = isArrayOfRenderedColumns(_eval.rendered_columns); const inputOutputSection = ( <> - {result.expected ? ( + {_eval.expected ? ( <> @@ -168,12 +164,12 @@ function ResultComponent() { title="Output" description="The output of the task." copyableText={ - typeof result.output === "string" ? result.output : undefined + typeof _eval.output === "string" ? _eval.output : undefined } > @@ -184,7 +180,7 @@ function ResultComponent() {