@post-print/agent-test 2.0.4 → 2.0.6
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/sdk/criterion-provenance.d.ts +7 -0
- package/dist/sdk/criterion-provenance.d.ts.map +1 -0
- package/dist/sdk/criterion-provenance.js +29 -0
- package/dist/sdk/criterion-provenance.js.map +1 -0
- package/dist/sdk/execution-store.d.ts +1 -0
- package/dist/sdk/execution-store.d.ts.map +1 -1
- package/dist/sdk/execution-store.js +8 -2
- package/dist/sdk/execution-store.js.map +1 -1
- package/dist/sdk/expect.d.ts.map +1 -1
- package/dist/sdk/expect.js +29 -1
- package/dist/sdk/expect.js.map +1 -1
- package/dist/sdk/judge.d.ts +8 -0
- package/dist/sdk/judge.d.ts.map +1 -1
- package/dist/sdk/judge.js +5 -1
- package/dist/sdk/judge.js.map +1 -1
- package/dist/sdk/runtime.d.ts +1 -0
- package/dist/sdk/runtime.d.ts.map +1 -1
- package/dist/sdk/runtime.js +58 -27
- package/dist/sdk/runtime.js.map +1 -1
- package/dist/viewer/client-entry.js +24 -24
- package/dist/viewer/stylex.css +75 -4
- package/dist/viewer/v2-client.d.ts.map +1 -1
- package/dist/viewer/v2-client.js +402 -43
- package/dist/viewer/v2-client.js.map +1 -1
- package/package.json +2 -2
package/dist/viewer/v2-client.js
CHANGED
|
@@ -2,7 +2,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
2
2
|
import * as stylex from "@stylexjs/stylex";
|
|
3
3
|
import { useMutation, useQueries, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
4
4
|
import { createRootRoute, createRoute, createRouter, Link, Outlet, useNavigate, } from "@tanstack/react-router";
|
|
5
|
-
import { createContext, useContext, useState } from "react";
|
|
5
|
+
import { createContext, useContext, useId, useState, } from "react";
|
|
6
6
|
import Markdown from "react-markdown";
|
|
7
7
|
import { viewerApi } from "./generated-api.js";
|
|
8
8
|
import { assertionComparison, conversationPlaceholder, preferredExecutionId, stripAnsi, } from "./presentation.js";
|
|
@@ -646,6 +646,11 @@ const styles = stylex.create({
|
|
|
646
646
|
},
|
|
647
647
|
metricValue: { color: "var(--text)", fontSize: "0.77rem", fontWeight: 700 },
|
|
648
648
|
conversation: {
|
|
649
|
+
display: "grid",
|
|
650
|
+
gap: "0.55rem",
|
|
651
|
+
padding: "0.2rem 0",
|
|
652
|
+
},
|
|
653
|
+
conversationSurface: {
|
|
649
654
|
display: "grid",
|
|
650
655
|
gap: "0.55rem",
|
|
651
656
|
padding: "0.75rem",
|
|
@@ -662,7 +667,7 @@ const styles = stylex.create({
|
|
|
662
667
|
conversationHeader: {
|
|
663
668
|
display: "flex",
|
|
664
669
|
alignItems: "center",
|
|
665
|
-
justifyContent: "
|
|
670
|
+
justifyContent: "flex-start",
|
|
666
671
|
gap: "0.75rem",
|
|
667
672
|
},
|
|
668
673
|
runTabs: { display: "inline-flex", alignItems: "center", gap: "0.12rem" },
|
|
@@ -678,34 +683,49 @@ const styles = stylex.create({
|
|
|
678
683
|
cursor: "pointer",
|
|
679
684
|
},
|
|
680
685
|
runTabActive: { backgroundColor: "var(--accent-soft)", color: "var(--accent)" },
|
|
681
|
-
conversationTabs: {
|
|
686
|
+
conversationTabs: {
|
|
687
|
+
display: "flex",
|
|
688
|
+
flexWrap: "wrap",
|
|
689
|
+
gap: "0.9rem",
|
|
690
|
+
padding: "0 0.15rem",
|
|
691
|
+
borderBottomWidth: 1,
|
|
692
|
+
borderBottomStyle: "solid",
|
|
693
|
+
borderBottomColor: "var(--border)",
|
|
694
|
+
},
|
|
682
695
|
conversationTab: {
|
|
683
696
|
display: "inline-flex",
|
|
684
697
|
alignItems: "center",
|
|
685
|
-
gap: "0.
|
|
698
|
+
gap: "0.3rem",
|
|
686
699
|
boxSizing: "border-box",
|
|
687
|
-
height: "1.
|
|
688
|
-
minHeight:
|
|
700
|
+
height: "1.85rem",
|
|
701
|
+
minHeight: "1.85rem",
|
|
689
702
|
lineHeight: 1,
|
|
690
|
-
padding: "0.
|
|
691
|
-
|
|
703
|
+
padding: "0.2rem 0.1rem",
|
|
704
|
+
marginBottom: -1,
|
|
705
|
+
fontSize: "0.58rem",
|
|
692
706
|
fontWeight: 750,
|
|
693
707
|
letterSpacing: "0.04em",
|
|
694
708
|
textTransform: "uppercase",
|
|
695
|
-
|
|
696
|
-
|
|
709
|
+
borderBottomWidth: 2,
|
|
710
|
+
borderBottomStyle: "solid",
|
|
711
|
+
borderBottomColor: "transparent",
|
|
712
|
+
borderRadius: 0,
|
|
713
|
+
backgroundColor: "transparent",
|
|
697
714
|
color: "var(--muted)",
|
|
698
715
|
cursor: "pointer",
|
|
699
716
|
},
|
|
700
717
|
conversationTabActive: {
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
color: "var(--pass)",
|
|
718
|
+
borderBottomColor: "var(--accent)",
|
|
719
|
+
color: "var(--text)",
|
|
704
720
|
},
|
|
705
721
|
conversationTabDot: {
|
|
706
722
|
width: "0.28rem",
|
|
707
723
|
height: "0.28rem",
|
|
708
724
|
borderRadius: 999,
|
|
725
|
+
flex: "none",
|
|
726
|
+
},
|
|
727
|
+
conversationTabDotDone: { backgroundColor: "var(--pass)" },
|
|
728
|
+
conversationTabDotRunning: {
|
|
709
729
|
backgroundColor: "var(--skip)",
|
|
710
730
|
animationName: runningPulse,
|
|
711
731
|
animationDuration: {
|
|
@@ -718,6 +738,7 @@ const styles = stylex.create({
|
|
|
718
738
|
},
|
|
719
739
|
animationTimingFunction: "ease-in-out",
|
|
720
740
|
},
|
|
741
|
+
conversationTabDotStopped: { backgroundColor: "var(--fail)" },
|
|
721
742
|
conversationThread: { display: "grid", gap: "0.55rem" },
|
|
722
743
|
runningIndicator: {
|
|
723
744
|
display: "inline-flex",
|
|
@@ -775,6 +796,106 @@ const styles = stylex.create({
|
|
|
775
796
|
textTransform: "uppercase",
|
|
776
797
|
},
|
|
777
798
|
messageContent: { margin: 0, color: "var(--text)", fontSize: "0.79rem", lineHeight: 1.5 },
|
|
799
|
+
judgeQuestion: {
|
|
800
|
+
display: "grid",
|
|
801
|
+
justifySelf: "stretch",
|
|
802
|
+
gap: "0.45rem",
|
|
803
|
+
padding: "0.7rem",
|
|
804
|
+
border: "1px solid oklch(0.75 0.12 175 / 0.3)",
|
|
805
|
+
borderRadius: 10,
|
|
806
|
+
backgroundColor: "var(--accent-soft)",
|
|
807
|
+
},
|
|
808
|
+
judgeQuestionText: {
|
|
809
|
+
margin: 0,
|
|
810
|
+
color: "var(--text)",
|
|
811
|
+
fontSize: "0.79rem",
|
|
812
|
+
lineHeight: 1.5,
|
|
813
|
+
},
|
|
814
|
+
judgeContext: {
|
|
815
|
+
display: "grid",
|
|
816
|
+
gap: "0.4rem",
|
|
817
|
+
paddingTop: "0.55rem",
|
|
818
|
+
borderTop: "1px solid oklch(0.75 0.12 175 / 0.22)",
|
|
819
|
+
},
|
|
820
|
+
judgeContextHeader: {
|
|
821
|
+
display: "flex",
|
|
822
|
+
alignItems: "baseline",
|
|
823
|
+
justifyContent: "space-between",
|
|
824
|
+
gap: "0.75rem",
|
|
825
|
+
color: "var(--text)",
|
|
826
|
+
fontSize: "0.7rem",
|
|
827
|
+
},
|
|
828
|
+
judgeContextNote: { margin: 0, color: "var(--muted)", fontSize: "0.68rem", lineHeight: 1.4 },
|
|
829
|
+
judgeContextGroup: { display: "grid", gap: "0.35rem" },
|
|
830
|
+
judgeContextGroupHeading: {
|
|
831
|
+
margin: 0,
|
|
832
|
+
color: "var(--subtle)",
|
|
833
|
+
fontSize: "0.64rem",
|
|
834
|
+
fontWeight: 750,
|
|
835
|
+
letterSpacing: "0.04em",
|
|
836
|
+
textTransform: "uppercase",
|
|
837
|
+
},
|
|
838
|
+
judgeContextTabs: {
|
|
839
|
+
display: "flex",
|
|
840
|
+
flexWrap: "nowrap",
|
|
841
|
+
gap: "0.55rem",
|
|
842
|
+
overflowX: "auto",
|
|
843
|
+
scrollbarWidth: "none",
|
|
844
|
+
"::-webkit-scrollbar": { display: "none" },
|
|
845
|
+
borderBottomWidth: 1,
|
|
846
|
+
borderBottomStyle: "solid",
|
|
847
|
+
borderBottomColor: "oklch(0.75 0.12 175 / 0.22)",
|
|
848
|
+
},
|
|
849
|
+
judgeContextTab: {
|
|
850
|
+
flex: "0 0 auto",
|
|
851
|
+
minHeight: "1.75rem",
|
|
852
|
+
padding: "0.25rem 0.05rem",
|
|
853
|
+
marginBottom: -1,
|
|
854
|
+
borderBottomWidth: 2,
|
|
855
|
+
borderBottomStyle: "solid",
|
|
856
|
+
borderBottomColor: "transparent",
|
|
857
|
+
backgroundColor: { default: "transparent", ":hover": "var(--panel-3)" },
|
|
858
|
+
color: "var(--muted)",
|
|
859
|
+
fontSize: "0.58rem",
|
|
860
|
+
fontWeight: 750,
|
|
861
|
+
whiteSpace: "nowrap",
|
|
862
|
+
cursor: "pointer",
|
|
863
|
+
},
|
|
864
|
+
judgeContextTabActive: { borderBottomColor: "var(--accent)", color: "var(--text)" },
|
|
865
|
+
judgeContextAnswer: {
|
|
866
|
+
display: "grid",
|
|
867
|
+
gap: "0.3rem",
|
|
868
|
+
padding: "0.5rem",
|
|
869
|
+
borderRadius: 6,
|
|
870
|
+
backgroundColor: "var(--bg)",
|
|
871
|
+
},
|
|
872
|
+
judgeContextList: { display: "grid", gap: "0.3rem", margin: 0 },
|
|
873
|
+
judgeContextItem: {
|
|
874
|
+
display: "grid",
|
|
875
|
+
gridTemplateColumns: {
|
|
876
|
+
default: "minmax(7.5rem, 0.3fr) minmax(0, 1fr)",
|
|
877
|
+
"@media (max-width: 620px)": "1fr",
|
|
878
|
+
},
|
|
879
|
+
gap: "0.65rem",
|
|
880
|
+
padding: "0.4rem 0.5rem",
|
|
881
|
+
borderRadius: 6,
|
|
882
|
+
backgroundColor: "var(--bg)",
|
|
883
|
+
},
|
|
884
|
+
judgeContextLabel: {
|
|
885
|
+
color: "var(--subtle)",
|
|
886
|
+
fontSize: "0.64rem",
|
|
887
|
+
fontWeight: 750,
|
|
888
|
+
letterSpacing: "0.03em",
|
|
889
|
+
textTransform: "uppercase",
|
|
890
|
+
},
|
|
891
|
+
judgeContextValue: {
|
|
892
|
+
margin: 0,
|
|
893
|
+
color: "var(--text)",
|
|
894
|
+
fontSize: "0.72rem",
|
|
895
|
+
lineHeight: 1.45,
|
|
896
|
+
whiteSpace: "pre-wrap",
|
|
897
|
+
wordBreak: "break-word",
|
|
898
|
+
},
|
|
778
899
|
judgeResponse: {
|
|
779
900
|
display: "grid",
|
|
780
901
|
justifySelf: "start",
|
|
@@ -1221,15 +1342,139 @@ function ComparisonColumn({ execution, label }) {
|
|
|
1221
1342
|
return (_jsxs("article", { ...stylex.props(styles.card), children: [_jsxs("strong", { children: [label, ": ", execution.status] }), _jsx("p", { ...stylex.props(styles.meta), children: execution.id.slice(0, 8) }), execution.attempts.map((attempt) => (_jsx(AttemptCard, { attempt: attempt }, attempt.id)))] }));
|
|
1222
1343
|
}
|
|
1223
1344
|
function AttemptCard({ attempt, criteria = [] }) {
|
|
1224
|
-
const output =
|
|
1225
|
-
const
|
|
1226
|
-
const
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1345
|
+
const { operations, output, title } = attemptPresentation(attempt, criteria);
|
|
1346
|
+
const agents = operations.filter((operation) => operation.kind !== "evaluation");
|
|
1347
|
+
const judges = operations.filter((operation) => operation.kind === "evaluation");
|
|
1348
|
+
return (_jsxs("article", { ...stylex.props(styles.attemptCard), children: [_jsxs("div", { ...stylex.props(styles.row), children: [_jsxs("div", { children: [_jsx("strong", { ...stylex.props(styles.attemptTitle), children: title }), _jsxs("p", { ...stylex.props(styles.meta), children: [attempt.project || "default", " \u00B7 ", attempt.operations.length, " named", " ", pluralize(attempt.operations.length, "operation")] })] }), _jsxs("div", { ...stylex.props(styles.attemptActions), children: [attempt.status === "failed" ? (_jsx(CopyFailureButton, { attempt: attempt, criteria: criteria })) : null, _jsx("span", { ...stylex.props(styles.status, attempt.status === "passed" && styles.statusPass, attempt.status === "failed" && styles.statusFail, attempt.status === "running" && styles.statusRunning), children: attempt.status })] })] }), _jsx(OperationList, { operations: operations }), _jsx(ConversationSection, { label: "Agents", sectionOperations: agents, allOperations: operations, attempt: attempt, criteria: criteria }), _jsx(ConversationSection, { label: "Judges", sectionOperations: judges, allOperations: operations, attempt: attempt, criteria: criteria }), _jsx(TechnicalDetails, { operations: operations, output: output })] }));
|
|
1349
|
+
}
|
|
1350
|
+
function ConversationSection({ label, sectionOperations, allOperations, attempt, criteria, }) {
|
|
1351
|
+
const [selectedId, setSelectedId] = useState(sectionOperations[0]?.id);
|
|
1352
|
+
if (!sectionOperations.length && (allOperations.length > 0 || label === "Judges"))
|
|
1353
|
+
return null;
|
|
1354
|
+
const selected = sectionOperations.find((operation) => operation.id === selectedId) ?? sectionOperations[0];
|
|
1355
|
+
const status = attempt.status === "running" ? (selected?.status ?? attempt.status) : attempt.status;
|
|
1356
|
+
return (_jsx(Conversation, { label: label, operation: selected, operations: sectionOperations, allOperations: allOperations, select: setSelectedId, status: status, attemptStatus: attempt.status, result: _jsx(AttemptResult, { attempt: attempt, criteria: criteria, operation: selected, operations: allOperations, result: selectedOperationResult(selected) }) }));
|
|
1357
|
+
}
|
|
1358
|
+
function attemptPresentation(attempt, criteria) {
|
|
1359
|
+
return {
|
|
1360
|
+
operations: orderedAttemptOperations(attempt.operations, criteria),
|
|
1361
|
+
output: [...attempt.output.stdout, ...attempt.output.stderr].join("\n"),
|
|
1362
|
+
title: attempt.title.filter(Boolean).at(-1) ?? "Unnamed test",
|
|
1363
|
+
};
|
|
1364
|
+
}
|
|
1365
|
+
function selectedOperationResult(operation) {
|
|
1366
|
+
return operationResult(operation ? [operation] : []);
|
|
1367
|
+
}
|
|
1368
|
+
function orderedAttemptOperations(operations, criteria) {
|
|
1369
|
+
const entries = operations.map((operation, index) => ({
|
|
1370
|
+
operation,
|
|
1371
|
+
index,
|
|
1372
|
+
invocationIndex: operationInvocationIndex(operation),
|
|
1373
|
+
}));
|
|
1374
|
+
if (entries.some((entry) => entry.invocationIndex !== undefined))
|
|
1375
|
+
return entries
|
|
1376
|
+
.sort((left, right) => (left.invocationIndex ?? Number.POSITIVE_INFINITY) -
|
|
1377
|
+
(right.invocationIndex ?? Number.POSITIVE_INFINITY) || left.index - right.index)
|
|
1378
|
+
.map((entry) => entry.operation);
|
|
1379
|
+
return entries
|
|
1380
|
+
.sort((left, right) => {
|
|
1381
|
+
if (operationIdentity(left.operation) !== operationIdentity(right.operation))
|
|
1382
|
+
return left.index - right.index;
|
|
1383
|
+
const leftOrdinal = inferredOperationOrdinal(criteria, left.operation);
|
|
1384
|
+
const rightOrdinal = inferredOperationOrdinal(criteria, right.operation);
|
|
1385
|
+
if (leftOrdinal === undefined || rightOrdinal === undefined)
|
|
1386
|
+
return left.index - right.index;
|
|
1387
|
+
return leftOrdinal - rightOrdinal || left.index - right.index;
|
|
1388
|
+
})
|
|
1389
|
+
.map((entry) => entry.operation);
|
|
1390
|
+
}
|
|
1391
|
+
function operationInvocationIndex(operation) {
|
|
1392
|
+
if (typeof operation.invocationIndex === "number")
|
|
1393
|
+
return operation.invocationIndex;
|
|
1394
|
+
return operation.data.flatMap((value) => {
|
|
1395
|
+
if (!isRecord(value) || typeof value.invocationIndex !== "number")
|
|
1396
|
+
return [];
|
|
1397
|
+
return [value.invocationIndex];
|
|
1398
|
+
})[0];
|
|
1399
|
+
}
|
|
1400
|
+
function AttemptResult({ attempt, criteria, operation, operations, result, }) {
|
|
1401
|
+
const hasJudge = operations.some((item) => item.kind === "evaluation");
|
|
1402
|
+
const includeShared = attempt.status !== "running" && (operation?.kind === "evaluation" || !hasJudge);
|
|
1403
|
+
const indexes = selectedCriterionIndexes({ criteria, operation, operations, includeShared });
|
|
1404
|
+
return (_jsx(RunResult, { result: result, status: attempt.status, criteria: selectIndexes(criteria, indexes), explanations: selectOptionalIndexes(operationCriterionExplanations(criteria, operation), indexes), errors: attempt.errors, criterionResults: selectIndexes(attempt.criterionResults, indexes) }));
|
|
1405
|
+
}
|
|
1406
|
+
function selectedCriterionIndexes(input) {
|
|
1407
|
+
const { criteria, operation, operations, includeShared } = input;
|
|
1408
|
+
const recorded = operationCriterionIndexes(criteria, operation);
|
|
1409
|
+
const allRecorded = new Set(operations.flatMap((item) => operationCriterionIndexes(criteria, item)));
|
|
1410
|
+
if (allRecorded.size)
|
|
1411
|
+
return criteria.flatMap((_, index) => recorded.includes(index) || (includeShared && !allRecorded.has(index)) ? [index] : []);
|
|
1412
|
+
if (!includeShared)
|
|
1413
|
+
return [];
|
|
1414
|
+
return inferredRepeatedRunCriterionIndexes(criteria, operation, operations);
|
|
1415
|
+
}
|
|
1416
|
+
function operationCriterionIndexes(criteria, operation) {
|
|
1417
|
+
return [
|
|
1418
|
+
...new Set(operation?.data.flatMap((item) => criterionRecordIndexes(criteria, item)) ?? []),
|
|
1419
|
+
];
|
|
1420
|
+
}
|
|
1421
|
+
function criterionRecordIndexes(criteria, value) {
|
|
1422
|
+
if (!isRecord(value))
|
|
1423
|
+
return [];
|
|
1424
|
+
if (typeof value.criterion === "string") {
|
|
1425
|
+
const index = criteria.indexOf(value.criterion);
|
|
1426
|
+
return index < 0 ? [] : [index];
|
|
1427
|
+
}
|
|
1428
|
+
if (Array.isArray(value.criterionIndexes))
|
|
1429
|
+
return value.criterionIndexes.filter((index) => Number.isInteger(index));
|
|
1430
|
+
return Number.isInteger(value.criterionIndex) ? [Number(value.criterionIndex)] : [];
|
|
1431
|
+
}
|
|
1432
|
+
const RUN_ORDINALS = ["first", "second", "third", "fourth", "fifth"];
|
|
1433
|
+
const CRITERION_WORD_SEPARATOR = /[^a-z0-9]+/;
|
|
1434
|
+
function inferredRepeatedRunCriterionIndexes(criteria, operation, operations) {
|
|
1435
|
+
if (!operation)
|
|
1436
|
+
return undefined;
|
|
1437
|
+
const runs = operations.filter((item) => operationIdentity(item) === operationIdentity(operation));
|
|
1438
|
+
if (runs.length < 2)
|
|
1439
|
+
return undefined;
|
|
1440
|
+
const selected = inferredOperationOrdinal(criteria, operation) ??
|
|
1441
|
+
runs.findIndex((run) => run.id === operation.id);
|
|
1442
|
+
const scopes = criteria.map(criterionRunOrdinal);
|
|
1443
|
+
if (selected < 0 || !scopes.some((scope) => scope !== undefined))
|
|
1444
|
+
return undefined;
|
|
1445
|
+
return scopes.flatMap((scope, index) => scope === undefined || scope === selected ? [index] : []);
|
|
1446
|
+
}
|
|
1447
|
+
function inferredOperationOrdinal(criteria, operation) {
|
|
1448
|
+
const output = operationResult([operation]).output?.trim().toLowerCase();
|
|
1449
|
+
if (!output)
|
|
1450
|
+
return undefined;
|
|
1451
|
+
const matches = criteria.flatMap((criterion) => {
|
|
1452
|
+
const ordinal = criterionRunOrdinal(criterion);
|
|
1453
|
+
const score = criterionOutputScore(criterion, output);
|
|
1454
|
+
return ordinal === undefined || score === 0 ? [] : [{ ordinal, score }];
|
|
1455
|
+
});
|
|
1456
|
+
const bestScore = Math.max(0, ...matches.map((match) => match.score));
|
|
1457
|
+
const ordinals = new Set(matches.filter((match) => match.score === bestScore).map((match) => match.ordinal));
|
|
1458
|
+
return ordinals.size === 1 ? [...ordinals][0] : undefined;
|
|
1459
|
+
}
|
|
1460
|
+
function criterionOutputScore(criterion, output) {
|
|
1461
|
+
const normalized = criterion.toLowerCase();
|
|
1462
|
+
if (normalized.includes(`exactly ${output}`))
|
|
1463
|
+
return 2;
|
|
1464
|
+
return normalized.split(CRITERION_WORD_SEPARATOR).includes(output) ? 1 : 0;
|
|
1465
|
+
}
|
|
1466
|
+
function criterionRunOrdinal(criterion) {
|
|
1467
|
+
const normalized = criterion.toLowerCase();
|
|
1468
|
+
const index = RUN_ORDINALS.findIndex((ordinal) => normalized.includes(`${ordinal} `));
|
|
1469
|
+
return index < 0 ? undefined : index;
|
|
1470
|
+
}
|
|
1471
|
+
function selectIndexes(values, indexes) {
|
|
1472
|
+
return indexes
|
|
1473
|
+
? indexes.flatMap((index) => (values[index] === undefined ? [] : [values[index]]))
|
|
1474
|
+
: values;
|
|
1475
|
+
}
|
|
1476
|
+
function selectOptionalIndexes(values, indexes) {
|
|
1477
|
+
return indexes ? indexes.map((index) => values[index]) : values;
|
|
1233
1478
|
}
|
|
1234
1479
|
function CopyFailureButton({ attempt, criteria }) {
|
|
1235
1480
|
const [state, setState] = useState("idle");
|
|
@@ -1570,9 +1815,9 @@ function ResultMetrics({ result }) {
|
|
|
1570
1815
|
];
|
|
1571
1816
|
return (_jsx("div", { ...stylex.props(styles.metricGrid), children: metrics.map(([label, value]) => (_jsxs("div", { ...stylex.props(styles.metric), children: [_jsx("span", { ...stylex.props(styles.resourceFactLabel), children: label }), _jsx("span", { ...stylex.props(styles.metricValue), children: value })] }, label))) }));
|
|
1572
1817
|
}
|
|
1573
|
-
function Conversation({ operation, operations, select, status, }) {
|
|
1818
|
+
function Conversation({ label, operation, operations, allOperations, select, status, attemptStatus, result, }) {
|
|
1574
1819
|
const { timeline, judge } = conversationContent(operation, operations);
|
|
1575
|
-
return (
|
|
1820
|
+
return (_jsx("section", { "aria-label": label, ...stylex.props(styles.conversation), children: _jsxs("div", { ...stylex.props(styles.conversationSurface), children: [_jsx("header", { ...stylex.props(styles.conversationHeader), children: _jsx("h3", { ...stylex.props(styles.contentHeading), children: label }) }), _jsx(ConversationTabs, { label: label, operations: operations, selected: operation, select: select, attemptStatus: attemptStatus }), _jsx(ConversationRunTabs, { operation: operation, operations: operations, select: select }), operation?.kind === "evaluation" ? null : result, _jsxs("section", { "aria-label": `${label} messages`, ...stylex.props(styles.conversationThread), children: [judge ? (_jsx(JudgeQuestion, { review: judge, operations: allOperations })) : (_jsx(ConversationEvents, { timeline: timeline })), _jsx(JudgeResponse, { review: judge, operations: allOperations })] }), timeline.length === 0 && status !== "running" ? (_jsx("p", { ...stylex.props(styles.meta), children: conversationPlaceholder(status) })) : null, status === "running" ? _jsx(RunningIndicator, { kind: operation?.kind }) : null] }) }));
|
|
1576
1821
|
}
|
|
1577
1822
|
function conversationContent(operation, operations) {
|
|
1578
1823
|
if (operation?.kind === "evaluation") {
|
|
@@ -1604,13 +1849,89 @@ function recordedTimeline(result) {
|
|
|
1604
1849
|
function ConversationEvents({ timeline }) {
|
|
1605
1850
|
return timeline.map((event, index) => event.kind === "tool" ? (_jsx(ToolActivity, { tool: event.tool }, `tool:${event.tool.seq ?? index}:${event.tool.name}`)) : (_jsx(MessageBubble, { message: event.message }, `message:${event.message.seq ?? index}:${event.message.role}`)));
|
|
1606
1851
|
}
|
|
1607
|
-
function
|
|
1852
|
+
function JudgeQuestion({ review, operations, }) {
|
|
1853
|
+
if (!review.question && !review.context.length)
|
|
1854
|
+
return null;
|
|
1855
|
+
return (_jsxs("section", { "aria-label": "Judge question", ...stylex.props(styles.judgeQuestion), children: [_jsx("h4", { ...stylex.props(styles.judgeResponseHeading), children: "Judge question" }), review.question ? (_jsx("p", { ...stylex.props(styles.judgeQuestionText), children: review.question })) : null, review.context.length ? (_jsx(JudgeContext, { context: review.context, operations: operations })) : null] }));
|
|
1856
|
+
}
|
|
1857
|
+
function JudgeContext({ context, operations, }) {
|
|
1858
|
+
const { answers, supplied } = judgeContextGroups(context, operations);
|
|
1859
|
+
const [selectedId, setSelectedId] = useState(answers[0]?.id);
|
|
1860
|
+
const selected = answers.find((item) => item.id === selectedId) ?? answers[0];
|
|
1861
|
+
const panelId = useId();
|
|
1862
|
+
return (_jsxs("section", { "aria-label": "Context included", ...stylex.props(styles.judgeContext), children: [_jsxs("div", { ...stylex.props(styles.judgeContextHeader), children: [_jsx("strong", { children: "Context included" }), _jsxs("span", { children: [context.length, " selected fields"] })] }), _jsx("p", { ...stylex.props(styles.judgeContextNote), children: "Only these explicitly selected fields were sent to this judge." }), answers.length ? (_jsxs("section", { "aria-label": "Agent answers", ...stylex.props(styles.judgeContextGroup), children: [_jsx("h5", { ...stylex.props(styles.judgeContextGroupHeading), children: "Agent answers" }), _jsx("div", { role: "tablist", "aria-label": "Agent answers included", ...stylex.props(styles.judgeContextTabs), children: answers.map((answer, index) => (_jsx("button", { type: "button", role: "tab", "aria-selected": answer.id === selected?.id, "aria-controls": panelId, ...stylex.props(styles.buttonReset, styles.judgeContextTab, answer.id === selected?.id && styles.judgeContextTabActive), onClick: () => setSelectedId(answer.id), onKeyDown: (event) => selectContextWithKeyboard({ event, answers, index, select: setSelectedId }), children: answer.tabLabel }, answer.id))) }), selected ? (_jsx("div", { id: panelId, role: "tabpanel", "aria-label": `Agent response from ${selected.tabLabel}`, ...stylex.props(styles.judgeContextAnswer), children: _jsx("div", { ...stylex.props(styles.judgeContextValue), children: _jsx("div", { className: "message-markdown", children: _jsx(Markdown, { children: normalizeTranscriptMarkdown(selected.value) }) }) }) })) : null] })) : null, supplied.length ? (_jsxs("section", { "aria-label": "Additional context", ...stylex.props(styles.judgeContextGroup), children: [_jsx("h5", { ...stylex.props(styles.judgeContextGroupHeading), children: "Additional context" }), _jsx("dl", { ...stylex.props(styles.judgeContextList), children: supplied.map((item) => (_jsxs("div", { ...stylex.props(styles.judgeContextItem), children: [_jsx("dt", { ...stylex.props(styles.judgeContextLabel), children: item.label }), _jsx("dd", { ...stylex.props(styles.judgeContextValue), children: item.value })] }, item.label))) })] })) : null] }));
|
|
1863
|
+
}
|
|
1864
|
+
function judgeContextGroups(context, operations) {
|
|
1865
|
+
const candidates = operations
|
|
1866
|
+
.filter((operation) => operation.kind !== "evaluation")
|
|
1867
|
+
.map((operation) => ({ operation, output: operationOutput(operation) }));
|
|
1868
|
+
const claimed = new Set();
|
|
1869
|
+
const answers = [];
|
|
1870
|
+
const supplied = [];
|
|
1871
|
+
for (const [index, item] of context.entries()) {
|
|
1872
|
+
const match = candidates.find((candidate) => !claimed.has(candidate.operation.id) && sameContextValue(item.rawValue, candidate.output));
|
|
1873
|
+
if (!match) {
|
|
1874
|
+
supplied.push(item);
|
|
1875
|
+
continue;
|
|
1876
|
+
}
|
|
1877
|
+
claimed.add(match.operation.id);
|
|
1878
|
+
answers.push({
|
|
1879
|
+
...item,
|
|
1880
|
+
id: `${match.operation.id}:${index}`,
|
|
1881
|
+
tabLabel: agentRunLabel(match.operation, operations),
|
|
1882
|
+
});
|
|
1883
|
+
}
|
|
1884
|
+
return { answers, supplied };
|
|
1885
|
+
}
|
|
1886
|
+
function operationOutput(operation) {
|
|
1887
|
+
const records = operation.data.filter(isRecord);
|
|
1888
|
+
return [...records].reverse().find((record) => record.output !== undefined)?.output;
|
|
1889
|
+
}
|
|
1890
|
+
function sameContextValue(left, right) {
|
|
1891
|
+
if (right === undefined)
|
|
1892
|
+
return false;
|
|
1893
|
+
if (left === right)
|
|
1894
|
+
return true;
|
|
1895
|
+
try {
|
|
1896
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
1897
|
+
}
|
|
1898
|
+
catch {
|
|
1899
|
+
return false;
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
function agentRunLabel(operation, operations) {
|
|
1903
|
+
const peers = operations.filter((item) => item.kind !== "evaluation" && operationIdentity(item) === operationIdentity(operation));
|
|
1904
|
+
const name = conversationLabel(operation);
|
|
1905
|
+
if (peers.length < 2)
|
|
1906
|
+
return name;
|
|
1907
|
+
return `${name} · Run ${peers.findIndex((item) => item.id === operation.id) + 1}`;
|
|
1908
|
+
}
|
|
1909
|
+
function selectContextWithKeyboard(input) {
|
|
1910
|
+
const { event, answers, index, select } = input;
|
|
1911
|
+
if (event.key !== "ArrowLeft" && event.key !== "ArrowRight")
|
|
1912
|
+
return;
|
|
1913
|
+
event.preventDefault();
|
|
1914
|
+
const direction = event.key === "ArrowRight" ? 1 : -1;
|
|
1915
|
+
const nextIndex = (index + direction + answers.length) % answers.length;
|
|
1916
|
+
const next = answers[nextIndex];
|
|
1917
|
+
if (!next)
|
|
1918
|
+
return;
|
|
1919
|
+
select(next.id);
|
|
1920
|
+
event.currentTarget.parentElement
|
|
1921
|
+
?.querySelectorAll("[role=tab]")[nextIndex]?.focus();
|
|
1922
|
+
}
|
|
1923
|
+
function JudgeResponse({ review, operations, }) {
|
|
1608
1924
|
if (!review || (!review.outcomes.length && !review.reason))
|
|
1609
1925
|
return null;
|
|
1610
|
-
|
|
1926
|
+
const { answers } = judgeContextGroups(review.context, operations);
|
|
1927
|
+
return _jsx(JudgeFindings, { outcomes: review.outcomes, reason: review.reason, answers: answers });
|
|
1611
1928
|
}
|
|
1612
|
-
function JudgeFindings({ outcomes, reason, }) {
|
|
1613
|
-
return (_jsxs("section", { "aria-label": "Judge response", ...stylex.props(styles.judgeResponse), children: [_jsx("h4", { ...stylex.props(styles.judgeResponseHeading), children: "Judge response" }), _jsx("div", { ...stylex.props(styles.judgeOutcomeList), children: outcomes.map((outcome) => (_jsxs("div", { ...stylex.props(styles.judgeOutcome), children: [_jsx(CriterionOutcomeIcon, { outcome: outcome.passed ? "passed" : "failed", label: outcome.passed ? "Passed" : "Failed" }), _jsx("span", { ...stylex.props(styles.judgeOutcomeLabel), children: outcome.label }), outcome.explanation ? (_jsx("p", { ...stylex.props(styles.judgeOutcomeExplanation), children: outcome.explanation })) : null] }, outcome.label))) }), reason && !outcomes.length ? (_jsx("p", { ...stylex.props(styles.judgeOutcomeExplanation), children: reason })) : null] }));
|
|
1929
|
+
function JudgeFindings({ outcomes, reason, answers = [], }) {
|
|
1930
|
+
return (_jsxs("section", { "aria-label": "Judge response", ...stylex.props(styles.judgeResponse), children: [_jsx("h4", { ...stylex.props(styles.judgeResponseHeading), children: "Judge response" }), _jsx("div", { ...stylex.props(styles.judgeOutcomeList), children: outcomes.map((outcome) => (_jsxs("div", { ...stylex.props(styles.judgeOutcome), children: [_jsx(CriterionOutcomeIcon, { outcome: outcome.passed ? "passed" : "failed", label: outcome.passed ? "Passed" : "Failed" }), _jsx("span", { ...stylex.props(styles.judgeOutcomeLabel), children: judgeOutcomeLabel(outcome.label, answers) }), outcome.explanation ? (_jsx("p", { ...stylex.props(styles.judgeOutcomeExplanation), children: outcome.explanation })) : null] }, outcome.label))) }), reason && !outcomes.length ? (_jsx("p", { ...stylex.props(styles.judgeOutcomeExplanation), children: reason })) : null] }));
|
|
1931
|
+
}
|
|
1932
|
+
function judgeOutcomeLabel(label, answers) {
|
|
1933
|
+
const answer = answers.find((item) => label === `${item.label} Correct` || label === item.label);
|
|
1934
|
+
return answer?.tabLabel ?? label;
|
|
1614
1935
|
}
|
|
1615
1936
|
function judgeReviewData(operation) {
|
|
1616
1937
|
const records = operation.data.filter(isRecord);
|
|
@@ -1619,10 +1940,31 @@ function judgeReviewData(operation) {
|
|
|
1619
1940
|
const reason = optionalString(output.reason) ?? conversationOutput(record?.output);
|
|
1620
1941
|
return {
|
|
1621
1942
|
question: judgeQuestion(record),
|
|
1943
|
+
context: judgeContext(record?.input),
|
|
1622
1944
|
outcomes: judgeOutcomes(output, reason),
|
|
1623
1945
|
reason,
|
|
1624
1946
|
};
|
|
1625
1947
|
}
|
|
1948
|
+
function judgeContext(value) {
|
|
1949
|
+
if (value === undefined)
|
|
1950
|
+
return [];
|
|
1951
|
+
if (!isRecord(value))
|
|
1952
|
+
return [{ label: "Selected input", value: judgeContextValue(value), rawValue: value }];
|
|
1953
|
+
return Object.entries(value).map(([key, item]) => ({
|
|
1954
|
+
label: humanizeKey(key),
|
|
1955
|
+
value: judgeContextValue(item),
|
|
1956
|
+
rawValue: item,
|
|
1957
|
+
}));
|
|
1958
|
+
}
|
|
1959
|
+
function judgeContextValue(value) {
|
|
1960
|
+
if (typeof value === "string")
|
|
1961
|
+
return value;
|
|
1962
|
+
if (value === null || value === undefined)
|
|
1963
|
+
return String(value);
|
|
1964
|
+
if (typeof value === "number" || typeof value === "boolean")
|
|
1965
|
+
return String(value);
|
|
1966
|
+
return JSON.stringify(value, null, 2);
|
|
1967
|
+
}
|
|
1626
1968
|
function judgeQuestion(record) {
|
|
1627
1969
|
const evaluation = isRecord(record?.evaluation) ? record.evaluation : undefined;
|
|
1628
1970
|
return optionalString(evaluation?.prompt);
|
|
@@ -1650,10 +1992,16 @@ function judgeOutcomeExplanation(output, key) {
|
|
|
1650
1992
|
optionalString(output[`${key}Explanation`]) ??
|
|
1651
1993
|
optionalString(output[`${key}Reason`]));
|
|
1652
1994
|
}
|
|
1653
|
-
function
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1995
|
+
function operationCriterionExplanations(criteria, operation) {
|
|
1996
|
+
const explanations = Array(criteria.length);
|
|
1997
|
+
if (operation?.kind !== "evaluation")
|
|
1998
|
+
return explanations;
|
|
1999
|
+
const indexes = operationCriterionIndexes(criteria, operation);
|
|
2000
|
+
const outcomes = judgeReviewData(operation).outcomes;
|
|
2001
|
+
indexes.forEach((criterionIndex, outcomeIndex) => {
|
|
2002
|
+
explanations[criterionIndex] = outcomes[outcomeIndex]?.explanation;
|
|
2003
|
+
});
|
|
2004
|
+
return explanations;
|
|
1657
2005
|
}
|
|
1658
2006
|
function legacyJudgeInput(operation, operations) {
|
|
1659
2007
|
if (!operation || operation.kind !== "evaluation")
|
|
@@ -1681,25 +2029,24 @@ function legacyJudgeInput(operation, operations) {
|
|
|
1681
2029
|
});
|
|
1682
2030
|
return timeline;
|
|
1683
2031
|
}
|
|
1684
|
-
function ConversationTabs({ operations, selected, select, attemptStatus, }) {
|
|
2032
|
+
function ConversationTabs({ label, operations, selected, select, attemptStatus, }) {
|
|
1685
2033
|
if (!operations.length)
|
|
1686
2034
|
return null;
|
|
1687
2035
|
const groups = conversationGroups(operations);
|
|
1688
2036
|
const representatives = groups.map((group) => group.operations[0]);
|
|
1689
2037
|
const selectedGroup = selected ? operationIdentity(selected) : undefined;
|
|
1690
|
-
return (_jsx("div", { role: "tablist", "aria-label":
|
|
1691
|
-
group.operations.some((operation) => operation.status === "running"), onSelect: () => select(group.operations[0].id), onKeyDown: (event) => selectConversationWithKeyboard({
|
|
2038
|
+
return (_jsx("div", { role: "tablist", "aria-label": `${label} participants`, ...stylex.props(styles.conversationSwitcher, styles.conversationTabs), children: groups.map((group, index) => (_jsx(ConversationTab, { operation: group.operations[0], active: group.key === selectedGroup, status: conversationGroupStatus(group.operations, attemptStatus), onSelect: () => select(group.operations[0].id), onKeyDown: (event) => selectConversationWithKeyboard({
|
|
1692
2039
|
event,
|
|
1693
2040
|
operations: representatives,
|
|
1694
2041
|
index,
|
|
1695
2042
|
select,
|
|
1696
2043
|
}) }, group.key))) }));
|
|
1697
2044
|
}
|
|
1698
|
-
function ConversationTab({ operation, active,
|
|
1699
|
-
return (_jsxs("button", { type: "button", role: "tab", "aria-label": conversationLabel(operation), "aria-selected": active, ...stylex.props(styles.buttonReset, styles.
|
|
2045
|
+
function ConversationTab({ operation, active, status, onSelect, onKeyDown, }) {
|
|
2046
|
+
return (_jsxs("button", { type: "button", role: "tab", "aria-label": conversationLabel(operation), "aria-selected": active, ...stylex.props(styles.buttonReset, styles.conversationTab, active && styles.conversationTabActive), onClick: onSelect, onKeyDown: onKeyDown, children: [_jsx("span", { role: "status", "aria-label": `${operation?.name ?? "Operation"} is ${status}`, ...stylex.props(styles.conversationTabDot, status === "done" && styles.conversationTabDotDone, status === "running" && styles.conversationTabDotRunning, status === "stopped" && styles.conversationTabDotStopped) }), conversationLabel(operation)] }));
|
|
1700
2047
|
}
|
|
1701
2048
|
function conversationLabel(operation) {
|
|
1702
|
-
return
|
|
2049
|
+
return operation?.name ?? "Unnamed";
|
|
1703
2050
|
}
|
|
1704
2051
|
function conversationGroups(operations) {
|
|
1705
2052
|
const groups = new Map();
|
|
@@ -1713,6 +2060,17 @@ function conversationGroups(operations) {
|
|
|
1713
2060
|
}
|
|
1714
2061
|
return [...groups.values()];
|
|
1715
2062
|
}
|
|
2063
|
+
function conversationGroupStatus(operations, attemptStatus) {
|
|
2064
|
+
if (operations.some((operation) => operation.status === "failed" || operation.status === "interrupted"))
|
|
2065
|
+
return "stopped";
|
|
2066
|
+
if (operations.some((operation) => operation.status === "running")) {
|
|
2067
|
+
if (attemptStatus === "running")
|
|
2068
|
+
return "running";
|
|
2069
|
+
if (attemptStatus === "interrupted" || attemptStatus === "failed")
|
|
2070
|
+
return "stopped";
|
|
2071
|
+
}
|
|
2072
|
+
return "done";
|
|
2073
|
+
}
|
|
1716
2074
|
function operationIdentity(operation) {
|
|
1717
2075
|
return `${operation.name ?? "Unnamed"}\u0000${operation.kind}`;
|
|
1718
2076
|
}
|
|
@@ -1737,8 +2095,9 @@ function selectConversationWithKeyboard(input) {
|
|
|
1737
2095
|
const tabs = event.currentTarget.parentElement?.querySelectorAll("[role=tab]");
|
|
1738
2096
|
tabs?.[(index + direction + operations.length) % operations.length]?.focus();
|
|
1739
2097
|
}
|
|
1740
|
-
function RunningIndicator() {
|
|
1741
|
-
|
|
2098
|
+
function RunningIndicator({ kind }) {
|
|
2099
|
+
const participant = kind === "evaluation" ? "Judge" : "Agent";
|
|
2100
|
+
return (_jsxs("div", { role: "status", "aria-label": `${participant} is running`, ...stylex.props(styles.runningIndicator), children: [_jsx("span", { children: "Running" }), _jsxs("span", { "aria-hidden": "true", ...stylex.props(styles.runningDots), children: [_jsx("span", { ...stylex.props(styles.runningDot), children: "\u2022" }), _jsx("span", { ...stylex.props(styles.runningDot, styles.runningDotSecond), children: "\u2022" }), _jsx("span", { ...stylex.props(styles.runningDot, styles.runningDotThird), children: "\u2022" })] })] }));
|
|
1742
2101
|
}
|
|
1743
2102
|
function ToolActivity({ tool }) {
|
|
1744
2103
|
const presentation = toolPresentation(tool);
|
|
@@ -1798,18 +2157,18 @@ function OperationList({ operations }) {
|
|
|
1798
2157
|
if (operations.length === 0)
|
|
1799
2158
|
return null;
|
|
1800
2159
|
const summaries = summarizeOperations(operations);
|
|
1801
|
-
return (_jsx("div", { ...stylex.props(styles.list), children: summaries.map((summary) => (_jsxs("div", { ...stylex.props(styles.meta), children: [summary.name,
|
|
2160
|
+
return (_jsx("div", { ...stylex.props(styles.list), children: summaries.map((summary) => (_jsxs("div", { ...stylex.props(styles.meta), children: [summary.name, summary.count > 1 ? ` ×${summary.count}` : ""] }, summary.key))) }));
|
|
1802
2161
|
}
|
|
1803
2162
|
function summarizeOperations(operations) {
|
|
1804
2163
|
const summaries = new Map();
|
|
1805
2164
|
for (const operation of operations) {
|
|
1806
2165
|
const name = operation.name ?? operation.kind;
|
|
1807
|
-
const key =
|
|
2166
|
+
const key = operationIdentity(operation);
|
|
1808
2167
|
const existing = summaries.get(key);
|
|
1809
2168
|
if (existing)
|
|
1810
2169
|
existing.count++;
|
|
1811
2170
|
else
|
|
1812
|
-
summaries.set(key, { key, name,
|
|
2171
|
+
summaries.set(key, { key, name, count: 1 });
|
|
1813
2172
|
}
|
|
1814
2173
|
return [...summaries.values()];
|
|
1815
2174
|
}
|