@arizeai/phoenix-client 6.10.1 → 6.11.1
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/README.md +62 -0
- package/dist/esm/__generated__/api/v1.d.ts +254 -2
- package/dist/esm/__generated__/api/v1.d.ts.map +1 -1
- package/dist/esm/jest/index.d.ts +5 -0
- package/dist/esm/jest/index.d.ts.map +1 -0
- package/dist/esm/jest/index.js +49 -0
- package/dist/esm/jest/index.js.map +1 -0
- package/dist/esm/jest/reporter.d.ts +13 -0
- package/dist/esm/jest/reporter.d.ts.map +1 -0
- package/dist/esm/jest/reporter.js +19 -0
- package/dist/esm/jest/reporter.js.map +1 -0
- package/dist/esm/prompts/sdks/toAI.d.ts +2 -2
- package/dist/esm/prompts/sdks/toAI.d.ts.map +1 -1
- package/dist/esm/prompts/sdks/toAI.js.map +1 -1
- package/dist/esm/prompts/sdks/toAnthropic.d.ts +2 -2
- package/dist/esm/prompts/sdks/toAnthropic.d.ts.map +1 -1
- package/dist/esm/prompts/sdks/toAnthropic.js.map +1 -1
- package/dist/esm/prompts/sdks/toOpenAI.d.ts +2 -2
- package/dist/esm/prompts/sdks/toOpenAI.d.ts.map +1 -1
- package/dist/esm/prompts/sdks/toOpenAI.js.map +1 -1
- package/dist/esm/prompts/sdks/toSDK.d.ts +8 -8
- package/dist/esm/prompts/sdks/toSDK.d.ts.map +1 -1
- package/dist/esm/prompts/sdks/toSDK.js.map +1 -1
- package/dist/esm/prompts/sdks/types.d.ts +2 -2
- package/dist/esm/prompts/sdks/types.d.ts.map +1 -1
- package/dist/esm/schemas/llm/anthropic/converters.d.ts +8 -8
- package/dist/esm/schemas/llm/anthropic/messagePartSchemas.d.ts +4 -4
- package/dist/esm/schemas/llm/anthropic/messageSchemas.d.ts +6 -6
- package/dist/esm/schemas/llm/constants.d.ts +3 -3
- package/dist/esm/schemas/llm/converters.d.ts +12 -12
- package/dist/esm/schemas/llm/openai/converters.d.ts +3 -3
- package/dist/esm/schemas/llm/schemas.d.ts +2 -2
- package/dist/esm/testing/acceptance.d.ts +20 -0
- package/dist/esm/testing/acceptance.d.ts.map +1 -0
- package/dist/esm/testing/acceptance.js +129 -0
- package/dist/esm/testing/acceptance.js.map +1 -0
- package/dist/esm/testing/define-api.d.ts +157 -0
- package/dist/esm/testing/define-api.d.ts.map +1 -0
- package/dist/esm/testing/define-api.js +78 -0
- package/dist/esm/testing/define-api.js.map +1 -0
- package/dist/esm/testing/helpers.d.ts +55 -0
- package/dist/esm/testing/helpers.d.ts.map +1 -0
- package/dist/esm/testing/helpers.js +179 -0
- package/dist/esm/testing/helpers.js.map +1 -0
- package/dist/esm/testing/phoenix-test-tracking.d.ts +68 -0
- package/dist/esm/testing/phoenix-test-tracking.d.ts.map +1 -0
- package/dist/esm/testing/phoenix-test-tracking.js +521 -0
- package/dist/esm/testing/phoenix-test-tracking.js.map +1 -0
- package/dist/esm/testing/report-artifacts.d.ts +45 -0
- package/dist/esm/testing/report-artifacts.d.ts.map +1 -0
- package/dist/esm/testing/report-artifacts.js +218 -0
- package/dist/esm/testing/report-artifacts.js.map +1 -0
- package/dist/esm/testing/report-run.d.ts +22 -0
- package/dist/esm/testing/report-run.d.ts.map +1 -0
- package/dist/esm/testing/report-run.js +41 -0
- package/dist/esm/testing/report-run.js.map +1 -0
- package/dist/esm/testing/reporter-format.d.ts +83 -0
- package/dist/esm/testing/reporter-format.d.ts.map +1 -0
- package/dist/esm/testing/reporter-format.js +852 -0
- package/dist/esm/testing/reporter-format.js.map +1 -0
- package/dist/esm/testing/runner.d.ts +31 -0
- package/dist/esm/testing/runner.d.ts.map +1 -0
- package/dist/esm/testing/runner.js +238 -0
- package/dist/esm/testing/runner.js.map +1 -0
- package/dist/esm/testing/state.d.ts +138 -0
- package/dist/esm/testing/state.d.ts.map +1 -0
- package/dist/esm/testing/state.js +31 -0
- package/dist/esm/testing/state.js.map +1 -0
- package/dist/esm/testing/types.d.ts +319 -0
- package/dist/esm/testing/types.d.ts.map +1 -0
- package/dist/esm/testing/types.js +9 -0
- package/dist/esm/testing/types.js.map +1 -0
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/esm/utils/channel.d.ts +7 -7
- package/dist/esm/utils/channel.d.ts.map +1 -1
- package/dist/esm/utils/channel.js +1 -1
- package/dist/esm/utils/channel.js.map +1 -1
- package/dist/esm/utils/formatPromptMessages.d.ts.map +1 -1
- package/dist/esm/utils/getPromptBySelector.d.ts.map +1 -1
- package/dist/esm/utils/promisifyResult.d.ts +1 -1
- package/dist/esm/utils/promisifyResult.d.ts.map +1 -1
- package/dist/esm/utils/promisifyResult.js.map +1 -1
- package/dist/esm/utils/schemaMatches.d.ts +5 -5
- package/dist/esm/utils/schemaMatches.d.ts.map +1 -1
- package/dist/esm/utils/schemaMatches.js.map +1 -1
- package/dist/esm/vitest/index.d.ts +5 -0
- package/dist/esm/vitest/index.d.ts.map +1 -0
- package/dist/esm/vitest/index.js +15 -0
- package/dist/esm/vitest/index.js.map +1 -0
- package/dist/esm/vitest/reporter.d.ts +19 -0
- package/dist/esm/vitest/reporter.d.ts.map +1 -0
- package/dist/esm/vitest/reporter.js +27 -0
- package/dist/esm/vitest/reporter.js.map +1 -0
- package/dist/src/__generated__/api/v1.d.ts +254 -2
- package/dist/src/__generated__/api/v1.d.ts.map +1 -1
- package/dist/src/jest/index.d.ts +5 -0
- package/dist/src/jest/index.d.ts.map +1 -0
- package/dist/src/jest/index.js +58 -0
- package/dist/src/jest/index.js.map +1 -0
- package/dist/src/jest/reporter.d.ts +13 -0
- package/dist/src/jest/reporter.d.ts.map +1 -0
- package/dist/src/jest/reporter.js +23 -0
- package/dist/src/jest/reporter.js.map +1 -0
- package/dist/src/prompts/sdks/toAI.d.ts +2 -2
- package/dist/src/prompts/sdks/toAI.d.ts.map +1 -1
- package/dist/src/prompts/sdks/toAI.js.map +1 -1
- package/dist/src/prompts/sdks/toAnthropic.d.ts +2 -2
- package/dist/src/prompts/sdks/toAnthropic.d.ts.map +1 -1
- package/dist/src/prompts/sdks/toAnthropic.js.map +1 -1
- package/dist/src/prompts/sdks/toOpenAI.d.ts +2 -2
- package/dist/src/prompts/sdks/toOpenAI.d.ts.map +1 -1
- package/dist/src/prompts/sdks/toOpenAI.js.map +1 -1
- package/dist/src/prompts/sdks/toSDK.d.ts +8 -8
- package/dist/src/prompts/sdks/toSDK.d.ts.map +1 -1
- package/dist/src/prompts/sdks/toSDK.js.map +1 -1
- package/dist/src/prompts/sdks/types.d.ts +2 -2
- package/dist/src/prompts/sdks/types.d.ts.map +1 -1
- package/dist/src/schemas/llm/anthropic/converters.d.ts +8 -8
- package/dist/src/schemas/llm/anthropic/messagePartSchemas.d.ts +4 -4
- package/dist/src/schemas/llm/anthropic/messageSchemas.d.ts +6 -6
- package/dist/src/schemas/llm/constants.d.ts +3 -3
- package/dist/src/schemas/llm/converters.d.ts +12 -12
- package/dist/src/schemas/llm/openai/converters.d.ts +3 -3
- package/dist/src/schemas/llm/schemas.d.ts +2 -2
- package/dist/src/testing/acceptance.d.ts +20 -0
- package/dist/src/testing/acceptance.d.ts.map +1 -0
- package/dist/src/testing/acceptance.js +114 -0
- package/dist/src/testing/acceptance.js.map +1 -0
- package/dist/src/testing/define-api.d.ts +157 -0
- package/dist/src/testing/define-api.d.ts.map +1 -0
- package/dist/src/testing/define-api.js +81 -0
- package/dist/src/testing/define-api.js.map +1 -0
- package/dist/src/testing/helpers.d.ts +55 -0
- package/dist/src/testing/helpers.d.ts.map +1 -0
- package/dist/src/testing/helpers.js +182 -0
- package/dist/src/testing/helpers.js.map +1 -0
- package/dist/src/testing/phoenix-test-tracking.d.ts +68 -0
- package/dist/src/testing/phoenix-test-tracking.d.ts.map +1 -0
- package/dist/src/testing/phoenix-test-tracking.js +530 -0
- package/dist/src/testing/phoenix-test-tracking.js.map +1 -0
- package/dist/src/testing/report-artifacts.d.ts +45 -0
- package/dist/src/testing/report-artifacts.d.ts.map +1 -0
- package/dist/src/testing/report-artifacts.js +225 -0
- package/dist/src/testing/report-artifacts.js.map +1 -0
- package/dist/src/testing/report-run.d.ts +22 -0
- package/dist/src/testing/report-run.d.ts.map +1 -0
- package/dist/src/testing/report-run.js +47 -0
- package/dist/src/testing/report-run.js.map +1 -0
- package/dist/src/testing/reporter-format.d.ts +83 -0
- package/dist/src/testing/reporter-format.d.ts.map +1 -0
- package/dist/src/testing/reporter-format.js +870 -0
- package/dist/src/testing/reporter-format.js.map +1 -0
- package/dist/src/testing/runner.d.ts +31 -0
- package/dist/src/testing/runner.d.ts.map +1 -0
- package/dist/src/testing/runner.js +258 -0
- package/dist/src/testing/runner.js.map +1 -0
- package/dist/src/testing/state.d.ts +138 -0
- package/dist/src/testing/state.d.ts.map +1 -0
- package/dist/src/testing/state.js +38 -0
- package/dist/src/testing/state.js.map +1 -0
- package/dist/src/testing/types.d.ts +319 -0
- package/dist/src/testing/types.d.ts.map +1 -0
- package/dist/src/testing/types.js +13 -0
- package/dist/src/testing/types.js.map +1 -0
- package/dist/src/utils/channel.d.ts +7 -7
- package/dist/src/utils/channel.d.ts.map +1 -1
- package/dist/src/utils/channel.js +1 -1
- package/dist/src/utils/channel.js.map +1 -1
- package/dist/src/utils/formatPromptMessages.d.ts.map +1 -1
- package/dist/src/utils/getPromptBySelector.d.ts.map +1 -1
- package/dist/src/utils/promisifyResult.d.ts +1 -1
- package/dist/src/utils/promisifyResult.d.ts.map +1 -1
- package/dist/src/utils/promisifyResult.js.map +1 -1
- package/dist/src/utils/schemaMatches.d.ts +5 -5
- package/dist/src/utils/schemaMatches.d.ts.map +1 -1
- package/dist/src/utils/schemaMatches.js.map +1 -1
- package/dist/src/vitest/index.d.ts +5 -0
- package/dist/src/vitest/index.d.ts.map +1 -0
- package/dist/src/vitest/index.js +23 -0
- package/dist/src/vitest/index.js.map +1 -0
- package/dist/src/vitest/reporter.d.ts +19 -0
- package/dist/src/vitest/reporter.d.ts.map +1 -0
- package/dist/src/vitest/reporter.js +34 -0
- package/dist/src/vitest/reporter.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/docs/ci-evals-annotations.mdx +190 -0
- package/docs/ci-evals-jest.mdx +78 -0
- package/docs/ci-evals-vitest.mdx +240 -0
- package/docs/ci-evals.mdx +263 -0
- package/docs/overview.mdx +9 -1
- package/package.json +49 -17
- package/src/__generated__/api/v1.ts +254 -2
- package/src/jest/index.ts +124 -0
- package/src/jest/reporter.ts +22 -0
- package/src/prompts/sdks/toAI.ts +4 -3
- package/src/prompts/sdks/toAnthropic.ts +4 -3
- package/src/prompts/sdks/toOpenAI.ts +4 -3
- package/src/prompts/sdks/toSDK.ts +16 -11
- package/src/prompts/sdks/types.ts +2 -2
- package/src/testing/acceptance.ts +190 -0
- package/src/testing/define-api.ts +279 -0
- package/src/testing/helpers.ts +251 -0
- package/src/testing/phoenix-test-tracking.ts +637 -0
- package/src/testing/report-artifacts.ts +272 -0
- package/src/testing/report-run.ts +44 -0
- package/src/testing/reporter-format.ts +1072 -0
- package/src/testing/runner.ts +350 -0
- package/src/testing/state.ts +165 -0
- package/src/testing/types.ts +366 -0
- package/src/utils/channel.ts +17 -15
- package/src/utils/promisifyResult.ts +6 -4
- package/src/utils/schemaMatches.ts +12 -10
- package/src/vitest/index.ts +57 -0
- package/src/vitest/reporter.ts +32 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import {
|
|
2
|
+
endTaskSpanForRun,
|
|
3
|
+
postAnnotation,
|
|
4
|
+
runEvaluatorWithTracing,
|
|
5
|
+
} from "./phoenix-test-tracking";
|
|
6
|
+
import { currentRun, type SuiteState } from "./state";
|
|
7
|
+
import type {
|
|
8
|
+
Annotation,
|
|
9
|
+
EvaluationParams,
|
|
10
|
+
EvaluationResult,
|
|
11
|
+
EvaluationResultObject,
|
|
12
|
+
Evaluator,
|
|
13
|
+
KVMap,
|
|
14
|
+
} from "./types";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Log the output produced by the test for the current run.
|
|
18
|
+
*
|
|
19
|
+
* Calling this multiple times overwrites the previously recorded value.
|
|
20
|
+
* The argument can be any JSON-serializable value — typically an object
|
|
21
|
+
* matching the shape of the example's `expected` field.
|
|
22
|
+
*/
|
|
23
|
+
export function logOutput(output: unknown): void {
|
|
24
|
+
const run = currentRun();
|
|
25
|
+
if (!run) {
|
|
26
|
+
throw new Error(
|
|
27
|
+
"logOutput() must be called inside a Phoenix eval test body"
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
run.output = output;
|
|
31
|
+
run.outputSet = true;
|
|
32
|
+
endTaskSpanForRun(run);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Record an annotation on the current run.
|
|
37
|
+
*
|
|
38
|
+
* Annotations are collected during the test and posted to Phoenix as
|
|
39
|
+
* experiment evaluations after the test completes. The `name` is the
|
|
40
|
+
* Phoenix evaluation name; `score`, `label`, and `explanation` map to
|
|
41
|
+
* the standard Phoenix `EvaluationResult` fields.
|
|
42
|
+
*
|
|
43
|
+
* The annotation name `"pass"` is reserved — Phoenix eval tests always write
|
|
44
|
+
* a `pass` annotation derived from the test's assertion outcome, so a
|
|
45
|
+
* user-supplied annotation with that name would race / overwrite the
|
|
46
|
+
* built-in one. Such calls are silently ignored.
|
|
47
|
+
*/
|
|
48
|
+
export function logAnnotation(annotation: Annotation): void {
|
|
49
|
+
const run = currentRun();
|
|
50
|
+
if (!run) {
|
|
51
|
+
throw new Error(
|
|
52
|
+
"logAnnotation() must be called inside a Phoenix eval test body"
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
if (annotation.name === "pass") return;
|
|
56
|
+
run.annotations.push(annotation);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Run an evaluator object against the current test run and record the result.
|
|
61
|
+
*
|
|
62
|
+
* The evaluator may come from `@arizeai/phoenix-evals.createEvaluator`,
|
|
63
|
+
* `asExperimentEvaluator`, or any plain object with `{ name, evaluate }`.
|
|
64
|
+
* When `params` is omitted, the current test's `input`, recorded `output`,
|
|
65
|
+
* `expected`, `metadata`, and task `traceId` are supplied.
|
|
66
|
+
*/
|
|
67
|
+
export async function evaluate<
|
|
68
|
+
Params extends KVMap = EvaluationParams & KVMap,
|
|
69
|
+
Result = EvaluationResult,
|
|
70
|
+
>(
|
|
71
|
+
evaluator: Evaluator<Params, Result>,
|
|
72
|
+
params?: Partial<Params> & KVMap
|
|
73
|
+
): Promise<Result> {
|
|
74
|
+
const run = currentRun();
|
|
75
|
+
if (!run) {
|
|
76
|
+
return await evaluator.evaluate((params ?? {}) as Params);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (!run.outputSet && !(params && "output" in params)) {
|
|
80
|
+
warnEvaluateBeforeOutput(run.suite, evaluator.name, run.testName);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const evaluatorParams = {
|
|
84
|
+
input: run.params.input,
|
|
85
|
+
// `run.output` is only ever set together with `outputSet`, so it is already
|
|
86
|
+
// `undefined` until a value is recorded.
|
|
87
|
+
output: run.output,
|
|
88
|
+
expected: run.params.expected,
|
|
89
|
+
metadata: run.params.metadata,
|
|
90
|
+
traceId: run.traceId ?? null,
|
|
91
|
+
...(params ?? {}),
|
|
92
|
+
} as unknown as Params;
|
|
93
|
+
|
|
94
|
+
const { result, traceId } = await runEvaluatorWithTracing(
|
|
95
|
+
run.suite,
|
|
96
|
+
evaluator.name,
|
|
97
|
+
evaluatorParams,
|
|
98
|
+
(paramsToEvaluate) => evaluator.evaluate(paramsToEvaluate)
|
|
99
|
+
);
|
|
100
|
+
logAnnotation(
|
|
101
|
+
toAnnotation({
|
|
102
|
+
name: evaluator.name,
|
|
103
|
+
kind: evaluator.kind,
|
|
104
|
+
result,
|
|
105
|
+
traceId,
|
|
106
|
+
})
|
|
107
|
+
);
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Trace an evaluator function so its execution shows up as a separate
|
|
113
|
+
* `EVALUATOR` span in Phoenix and any `{ name, score }`-shaped return
|
|
114
|
+
* value is automatically captured as an annotation on the current run.
|
|
115
|
+
*
|
|
116
|
+
* The annotation name defaults to the traced function's name, falling
|
|
117
|
+
* back to `"evaluator"`.
|
|
118
|
+
*/
|
|
119
|
+
export function traceEvaluator<EvaluatorParams extends KVMap, EvaluatorResult>(
|
|
120
|
+
fn: (params: EvaluatorParams) => EvaluatorResult | Promise<EvaluatorResult>,
|
|
121
|
+
options?: { name?: string }
|
|
122
|
+
): (params: EvaluatorParams) => Promise<EvaluatorResult> {
|
|
123
|
+
const evaluatorName =
|
|
124
|
+
options?.name ?? (fn.name && fn.name !== "" ? fn.name : "evaluator");
|
|
125
|
+
return async (params: EvaluatorParams) => {
|
|
126
|
+
const run = currentRun();
|
|
127
|
+
if (!run) {
|
|
128
|
+
// outside a test context, just call the function plainly
|
|
129
|
+
return await fn(params);
|
|
130
|
+
}
|
|
131
|
+
const { result, traceId } = await runEvaluatorWithTracing(
|
|
132
|
+
run.suite,
|
|
133
|
+
evaluatorName,
|
|
134
|
+
params,
|
|
135
|
+
fn
|
|
136
|
+
);
|
|
137
|
+
if (isAnnotationShaped(result)) {
|
|
138
|
+
logAnnotation({ ...result, traceId });
|
|
139
|
+
}
|
|
140
|
+
return result;
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Warn (at most once per suite) when an evaluator runs before any output was
|
|
146
|
+
* recorded and none was passed explicitly. Such an evaluator receives
|
|
147
|
+
* `output: undefined`, which silently scores against nothing — almost always a
|
|
148
|
+
* forgotten `logOutput()`. Harmless for evaluators that only read `input`.
|
|
149
|
+
*/
|
|
150
|
+
const warnedOutputSuites = new WeakSet<SuiteState>();
|
|
151
|
+
function warnEvaluateBeforeOutput(
|
|
152
|
+
suite: SuiteState,
|
|
153
|
+
evaluatorName: string,
|
|
154
|
+
testName: string
|
|
155
|
+
): void {
|
|
156
|
+
if (warnedOutputSuites.has(suite)) return;
|
|
157
|
+
warnedOutputSuites.add(suite);
|
|
158
|
+
// eslint-disable-next-line no-console
|
|
159
|
+
console.warn(
|
|
160
|
+
`[@arizeai/phoenix-client] evaluate("${evaluatorName}") ran before ` +
|
|
161
|
+
`logOutput() on test "${testName}", so the evaluator received ` +
|
|
162
|
+
`output=undefined. Call logOutput(...) first, or pass { output } ` +
|
|
163
|
+
`explicitly. (Ignore if this evaluator only needs input.)`
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Normalize an evaluator's return value into an {@link Annotation}. The value
|
|
169
|
+
* is already typed as an {@link EvaluationResult}, so we only dispatch on its
|
|
170
|
+
* runtime shape: a string becomes a `label`, a number/boolean/null becomes a
|
|
171
|
+
* `score`, and an object contributes its `score`/`label`/`explanation`/
|
|
172
|
+
* `metadata` directly.
|
|
173
|
+
*/
|
|
174
|
+
function toAnnotation({
|
|
175
|
+
name,
|
|
176
|
+
kind,
|
|
177
|
+
result,
|
|
178
|
+
traceId,
|
|
179
|
+
}: {
|
|
180
|
+
name: string;
|
|
181
|
+
kind?: Annotation["annotatorKind"];
|
|
182
|
+
result: unknown;
|
|
183
|
+
traceId?: string | null;
|
|
184
|
+
}): Annotation {
|
|
185
|
+
const annotatorKind = kind ?? "CODE";
|
|
186
|
+
if (typeof result === "string") {
|
|
187
|
+
return { name, label: result, annotatorKind, traceId };
|
|
188
|
+
}
|
|
189
|
+
if (
|
|
190
|
+
typeof result === "number" ||
|
|
191
|
+
typeof result === "boolean" ||
|
|
192
|
+
result === null
|
|
193
|
+
) {
|
|
194
|
+
return { name, score: result, annotatorKind, traceId };
|
|
195
|
+
}
|
|
196
|
+
if (typeof result === "object" && !Array.isArray(result)) {
|
|
197
|
+
const { score, label, explanation, metadata } =
|
|
198
|
+
result as EvaluationResultObject;
|
|
199
|
+
return {
|
|
200
|
+
name,
|
|
201
|
+
score,
|
|
202
|
+
label,
|
|
203
|
+
explanation,
|
|
204
|
+
metadata,
|
|
205
|
+
annotatorKind,
|
|
206
|
+
traceId,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
return { name, annotatorKind, traceId };
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function isAnnotationShaped(value: unknown): value is Annotation {
|
|
213
|
+
if (!value || typeof value !== "object") return false;
|
|
214
|
+
const v = value as { name?: unknown; score?: unknown };
|
|
215
|
+
if (typeof v.name !== "string") return false;
|
|
216
|
+
if (
|
|
217
|
+
v.score !== undefined &&
|
|
218
|
+
typeof v.score !== "number" &&
|
|
219
|
+
typeof v.score !== "boolean" &&
|
|
220
|
+
v.score !== null
|
|
221
|
+
) {
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Internal: persist all collected annotations for the run.
|
|
229
|
+
*
|
|
230
|
+
* Phoenix's `experiment_evaluations` endpoint is keyed by
|
|
231
|
+
* `(experiment_run_id, name)` so two annotations with the same name on
|
|
232
|
+
* the same run race each other. We collapse duplicates by name (last
|
|
233
|
+
* wins) up front, which makes the final state deterministic; the
|
|
234
|
+
* remaining writes target distinct names, so they post in parallel.
|
|
235
|
+
*/
|
|
236
|
+
export async function flushAnnotations(
|
|
237
|
+
runId: string | undefined,
|
|
238
|
+
annotations: Annotation[],
|
|
239
|
+
suite: SuiteState
|
|
240
|
+
): Promise<void> {
|
|
241
|
+
if (!annotations.length) return;
|
|
242
|
+
const byName = new Map<string, Annotation>();
|
|
243
|
+
for (const annotation of annotations) {
|
|
244
|
+
byName.set(annotation.name, annotation);
|
|
245
|
+
}
|
|
246
|
+
await Promise.all(
|
|
247
|
+
Array.from(byName.values(), (annotation) =>
|
|
248
|
+
postAnnotation(suite, runId, annotation)
|
|
249
|
+
)
|
|
250
|
+
);
|
|
251
|
+
}
|