@arizeai/phoenix-client 6.8.0 → 6.9.0
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 +77 -0
- package/dist/esm/__generated__/api/v1.d.ts +313 -19
- package/dist/esm/__generated__/api/v1.d.ts.map +1 -1
- package/dist/esm/constants/serverRequirements.d.ts +2 -0
- package/dist/esm/constants/serverRequirements.d.ts.map +1 -1
- package/dist/esm/constants/serverRequirements.js +15 -0
- package/dist/esm/constants/serverRequirements.js.map +1 -1
- package/dist/esm/datasets/appendDatasetExamples.d.ts +9 -3
- package/dist/esm/datasets/appendDatasetExamples.d.ts.map +1 -1
- package/dist/esm/datasets/appendDatasetExamples.js +23 -3
- package/dist/esm/datasets/appendDatasetExamples.js.map +1 -1
- package/dist/esm/datasets/createDataset.d.ts +4 -1
- package/dist/esm/datasets/createDataset.d.ts.map +1 -1
- package/dist/esm/datasets/createDataset.js +38 -3
- package/dist/esm/datasets/createDataset.js.map +1 -1
- package/dist/esm/datasets/getDatasetExamples.d.ts.map +1 -1
- package/dist/esm/datasets/getDatasetExamples.js +3 -2
- package/dist/esm/datasets/getDatasetExamples.js.map +1 -1
- package/dist/esm/datasets/index.d.ts +0 -1
- package/dist/esm/datasets/index.d.ts.map +1 -1
- package/dist/esm/datasets/index.js +0 -1
- package/dist/esm/datasets/index.js.map +1 -1
- package/dist/esm/experiments/resumeEvaluation.d.ts.map +1 -1
- package/dist/esm/experiments/resumeEvaluation.js +2 -1
- package/dist/esm/experiments/resumeEvaluation.js.map +1 -1
- package/dist/esm/experiments/resumeExperiment.d.ts.map +1 -1
- package/dist/esm/experiments/resumeExperiment.js +3 -2
- package/dist/esm/experiments/resumeExperiment.js.map +1 -1
- package/dist/esm/experiments/runExperiment.js +1 -1
- package/dist/esm/experiments/runExperiment.js.map +1 -1
- package/dist/esm/sessions/addSessionNote.d.ts +45 -0
- package/dist/esm/sessions/addSessionNote.d.ts.map +1 -0
- package/dist/esm/sessions/addSessionNote.js +45 -0
- package/dist/esm/sessions/addSessionNote.js.map +1 -0
- package/dist/esm/sessions/index.d.ts +1 -0
- package/dist/esm/sessions/index.d.ts.map +1 -1
- package/dist/esm/sessions/index.js +1 -0
- package/dist/esm/sessions/index.js.map +1 -1
- package/dist/esm/spans/addSpanNote.d.ts +5 -3
- package/dist/esm/spans/addSpanNote.d.ts.map +1 -1
- package/dist/esm/spans/addSpanNote.js +7 -4
- package/dist/esm/spans/addSpanNote.js.map +1 -1
- package/dist/esm/traces/addTraceAnnotation.d.ts +43 -0
- package/dist/esm/traces/addTraceAnnotation.d.ts.map +1 -0
- package/dist/esm/traces/addTraceAnnotation.js +43 -0
- package/dist/esm/traces/addTraceAnnotation.js.map +1 -0
- package/dist/esm/traces/addTraceNote.d.ts +5 -2
- package/dist/esm/traces/addTraceNote.d.ts.map +1 -1
- package/dist/esm/traces/addTraceNote.js +7 -3
- package/dist/esm/traces/addTraceNote.js.map +1 -1
- package/dist/esm/traces/index.d.ts +3 -0
- package/dist/esm/traces/index.d.ts.map +1 -1
- package/dist/esm/traces/index.js +2 -0
- package/dist/esm/traces/index.js.map +1 -1
- package/dist/esm/traces/logTraceAnnotations.d.ts +53 -0
- package/dist/esm/traces/logTraceAnnotations.d.ts.map +1 -0
- package/dist/esm/traces/logTraceAnnotations.js +50 -0
- package/dist/esm/traces/logTraceAnnotations.js.map +1 -0
- package/dist/esm/traces/types.d.ts +24 -0
- package/dist/esm/traces/types.d.ts.map +1 -0
- package/dist/esm/traces/types.js +38 -0
- package/dist/esm/traces/types.js.map +1 -0
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/esm/types/datasets.d.ts +10 -1
- package/dist/esm/types/datasets.d.ts.map +1 -1
- package/dist/esm/utils/apiErrorUtils.d.ts +2 -0
- package/dist/esm/utils/apiErrorUtils.d.ts.map +1 -0
- package/dist/esm/utils/apiErrorUtils.js +19 -0
- package/dist/esm/utils/apiErrorUtils.js.map +1 -0
- package/dist/esm/utils/formatPromptMessages.d.ts.map +1 -1
- package/dist/esm/utils/getPromptBySelector.d.ts.map +1 -1
- package/dist/src/__generated__/api/v1.d.ts +313 -19
- package/dist/src/__generated__/api/v1.d.ts.map +1 -1
- package/dist/src/constants/serverRequirements.d.ts +2 -0
- package/dist/src/constants/serverRequirements.d.ts.map +1 -1
- package/dist/src/constants/serverRequirements.js +16 -1
- package/dist/src/constants/serverRequirements.js.map +1 -1
- package/dist/src/datasets/appendDatasetExamples.d.ts +9 -3
- package/dist/src/datasets/appendDatasetExamples.d.ts.map +1 -1
- package/dist/src/datasets/appendDatasetExamples.js +24 -5
- package/dist/src/datasets/appendDatasetExamples.js.map +1 -1
- package/dist/src/datasets/createDataset.d.ts +4 -1
- package/dist/src/datasets/createDataset.d.ts.map +1 -1
- package/dist/src/datasets/createDataset.js +42 -5
- package/dist/src/datasets/createDataset.js.map +1 -1
- package/dist/src/datasets/getDatasetExamples.d.ts.map +1 -1
- package/dist/src/datasets/getDatasetExamples.js +15 -1
- package/dist/src/datasets/getDatasetExamples.js.map +1 -1
- package/dist/src/datasets/index.d.ts +0 -1
- package/dist/src/datasets/index.d.ts.map +1 -1
- package/dist/src/datasets/index.js +0 -1
- package/dist/src/datasets/index.js.map +1 -1
- package/dist/src/experiments/resumeEvaluation.d.ts.map +1 -1
- package/dist/src/experiments/resumeEvaluation.js +2 -1
- package/dist/src/experiments/resumeEvaluation.js.map +1 -1
- package/dist/src/experiments/resumeExperiment.d.ts.map +1 -1
- package/dist/src/experiments/resumeExperiment.js +3 -2
- package/dist/src/experiments/resumeExperiment.js.map +1 -1
- package/dist/src/experiments/runExperiment.js +1 -1
- package/dist/src/experiments/runExperiment.js.map +1 -1
- package/dist/src/sessions/addSessionNote.d.ts +45 -0
- package/dist/src/sessions/addSessionNote.d.ts.map +1 -0
- package/dist/src/sessions/addSessionNote.js +48 -0
- package/dist/src/sessions/addSessionNote.js.map +1 -0
- package/dist/src/sessions/index.d.ts +1 -0
- package/dist/src/sessions/index.d.ts.map +1 -1
- package/dist/src/sessions/index.js +1 -0
- package/dist/src/sessions/index.js.map +1 -1
- package/dist/src/spans/addSpanNote.d.ts +5 -3
- package/dist/src/spans/addSpanNote.d.ts.map +1 -1
- package/dist/src/spans/addSpanNote.js +7 -4
- package/dist/src/spans/addSpanNote.js.map +1 -1
- package/dist/src/traces/addTraceAnnotation.d.ts +43 -0
- package/dist/src/traces/addTraceAnnotation.d.ts.map +1 -0
- package/dist/src/traces/addTraceAnnotation.js +47 -0
- package/dist/src/traces/addTraceAnnotation.js.map +1 -0
- package/dist/src/traces/addTraceNote.d.ts +5 -2
- package/dist/src/traces/addTraceNote.d.ts.map +1 -1
- package/dist/src/traces/addTraceNote.js +7 -3
- package/dist/src/traces/addTraceNote.js.map +1 -1
- package/dist/src/traces/index.d.ts +3 -0
- package/dist/src/traces/index.d.ts.map +1 -1
- package/dist/src/traces/index.js +2 -0
- package/dist/src/traces/index.js.map +1 -1
- package/dist/src/traces/logTraceAnnotations.d.ts +53 -0
- package/dist/src/traces/logTraceAnnotations.d.ts.map +1 -0
- package/dist/src/traces/logTraceAnnotations.js +53 -0
- package/dist/src/traces/logTraceAnnotations.js.map +1 -0
- package/dist/src/traces/types.d.ts +24 -0
- package/dist/src/traces/types.d.ts.map +1 -0
- package/dist/src/traces/types.js +42 -0
- package/dist/src/traces/types.js.map +1 -0
- package/dist/src/types/datasets.d.ts +10 -1
- package/dist/src/types/datasets.d.ts.map +1 -1
- package/dist/src/utils/apiErrorUtils.d.ts +2 -0
- package/dist/src/utils/apiErrorUtils.d.ts.map +1 -0
- package/dist/src/utils/apiErrorUtils.js +22 -0
- package/dist/src/utils/apiErrorUtils.js.map +1 -0
- package/dist/src/utils/formatPromptMessages.d.ts.map +1 -1
- package/dist/src/utils/getPromptBySelector.d.ts.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/src/__generated__/api/v1.ts +313 -19
- package/src/constants/serverRequirements.ts +17 -0
- package/src/datasets/appendDatasetExamples.ts +26 -5
- package/src/datasets/createDataset.ts +66 -18
- package/src/datasets/getDatasetExamples.ts +7 -4
- package/src/datasets/index.ts +0 -1
- package/src/experiments/resumeEvaluation.ts +2 -1
- package/src/experiments/resumeExperiment.ts +3 -2
- package/src/experiments/runExperiment.ts +1 -1
- package/src/sessions/addSessionNote.ts +74 -0
- package/src/sessions/index.ts +1 -0
- package/src/spans/addSpanNote.ts +7 -4
- package/src/traces/addTraceAnnotation.ts +65 -0
- package/src/traces/addTraceNote.ts +7 -3
- package/src/traces/index.ts +3 -0
- package/src/traces/logTraceAnnotations.ts +75 -0
- package/src/traces/types.ts +72 -0
- package/src/types/datasets.ts +10 -1
- package/src/utils/apiErrorUtils.ts +17 -0
- package/dist/esm/datasets/createOrGetDataset.d.ts +0 -18
- package/dist/esm/datasets/createOrGetDataset.d.ts.map +0 -1
- package/dist/esm/datasets/createOrGetDataset.js +0 -29
- package/dist/esm/datasets/createOrGetDataset.js.map +0 -1
- package/dist/src/datasets/createOrGetDataset.d.ts +0 -18
- package/dist/src/datasets/createOrGetDataset.d.ts.map +0 -1
- package/dist/src/datasets/createOrGetDataset.js +0 -32
- package/dist/src/datasets/createOrGetDataset.js.map +0 -1
- package/src/datasets/createOrGetDataset.ts +0 -40
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import invariant from "tiny-invariant";
|
|
2
2
|
|
|
3
3
|
import { createClient } from "../client";
|
|
4
|
+
import { DATASET_UPLOAD_EXAMPLE_IDS } from "../constants/serverRequirements";
|
|
4
5
|
import type { ClientFn } from "../types/core";
|
|
5
6
|
import type { DatasetSelector, Example } from "../types/datasets";
|
|
7
|
+
import { ensureServerCapability } from "../utils/serverVersionUtils";
|
|
6
8
|
import { getDatasetInfo } from "./getDatasetInfo";
|
|
7
9
|
|
|
8
10
|
export type AppendDatasetExamplesParams = ClientFn & {
|
|
@@ -18,8 +20,7 @@ export type AppendDatasetExamplesParams = ClientFn & {
|
|
|
18
20
|
|
|
19
21
|
export type AppendDatasetExamplesResponse = {
|
|
20
22
|
datasetId: string;
|
|
21
|
-
|
|
22
|
-
// versionId: string;
|
|
23
|
+
versionId: string;
|
|
23
24
|
};
|
|
24
25
|
|
|
25
26
|
/**
|
|
@@ -36,19 +37,24 @@ export type AppendDatasetExamplesResponse = {
|
|
|
36
37
|
* - `metadata`: Optional metadata for the example
|
|
37
38
|
* - `splits`: Optional split assignment (string, array of strings, or null)
|
|
38
39
|
* - `spanId`: Optional OpenTelemetry span ID to link the example back to its source span
|
|
40
|
+
* - `id`: Optional stable ID for the example, used to reference or update it later
|
|
39
41
|
*
|
|
40
|
-
* @returns A promise that resolves to the dataset ID
|
|
42
|
+
* @returns A promise that resolves to the dataset ID and version ID
|
|
41
43
|
*
|
|
42
44
|
* @example
|
|
43
45
|
* ```ts
|
|
44
|
-
*
|
|
45
|
-
* const { datasetId } = await appendDatasetExamples({
|
|
46
|
+
* const { datasetId, versionId } = await appendDatasetExamples({
|
|
46
47
|
* dataset: { datasetName: "qa-dataset" },
|
|
47
48
|
* examples: [
|
|
48
49
|
* {
|
|
49
50
|
* input: { question: "What is deep learning?" },
|
|
50
51
|
* output: { answer: "Deep learning is..." },
|
|
51
52
|
* spanId: "span123abc" // Links to the source span
|
|
53
|
+
* },
|
|
54
|
+
* {
|
|
55
|
+
* id: "my-stable-id", // Stable ID for referencing this example later
|
|
56
|
+
* input: { question: "What is a transformer?" },
|
|
57
|
+
* output: { answer: "A transformer is..." },
|
|
52
58
|
* }
|
|
53
59
|
* ]
|
|
54
60
|
* });
|
|
@@ -73,6 +79,12 @@ export async function appendDatasetExamples({
|
|
|
73
79
|
// Only include span_ids in the request if at least one example has a span ID
|
|
74
80
|
const hasSpanIds = spanIds.some((id) => id !== null);
|
|
75
81
|
|
|
82
|
+
// Extract example IDs from examples, preserving null/undefined as null
|
|
83
|
+
const exampleIds = examples.map((example) => example?.id ?? null);
|
|
84
|
+
|
|
85
|
+
// Only include example_ids in the request if at least one example has an ID
|
|
86
|
+
const hasExampleIds = exampleIds.some((id) => id !== null);
|
|
87
|
+
|
|
76
88
|
let datasetName: string;
|
|
77
89
|
if ("datasetName" in dataset) {
|
|
78
90
|
datasetName = dataset.datasetName;
|
|
@@ -83,6 +95,12 @@ export async function appendDatasetExamples({
|
|
|
83
95
|
});
|
|
84
96
|
datasetName = datasetInfo.name;
|
|
85
97
|
}
|
|
98
|
+
if (hasExampleIds) {
|
|
99
|
+
await ensureServerCapability({
|
|
100
|
+
client,
|
|
101
|
+
requirement: DATASET_UPLOAD_EXAMPLE_IDS,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
86
104
|
const appendResponse = await client.POST("/v1/datasets/upload", {
|
|
87
105
|
params: {
|
|
88
106
|
query: {
|
|
@@ -97,11 +115,14 @@ export async function appendDatasetExamples({
|
|
|
97
115
|
metadata,
|
|
98
116
|
splits,
|
|
99
117
|
...(hasSpanIds ? { span_ids: spanIds } : {}),
|
|
118
|
+
...(hasExampleIds ? { example_ids: exampleIds } : {}),
|
|
100
119
|
},
|
|
101
120
|
});
|
|
102
121
|
invariant(appendResponse.data?.data, "Failed to append dataset examples");
|
|
103
122
|
const datasetId = appendResponse.data.data.dataset_id;
|
|
123
|
+
const versionId = appendResponse.data.data.version_id;
|
|
104
124
|
return {
|
|
105
125
|
datasetId,
|
|
126
|
+
versionId,
|
|
106
127
|
};
|
|
107
128
|
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import invariant from "tiny-invariant";
|
|
2
2
|
|
|
3
3
|
import { createClient } from "../client";
|
|
4
|
+
import { DATASET_UPLOAD_EXAMPLE_IDS } from "../constants/serverRequirements";
|
|
4
5
|
import type { ClientFn } from "../types/core";
|
|
5
6
|
import type { Example } from "../types/datasets";
|
|
7
|
+
import { ensureServerCapability } from "../utils/serverVersionUtils";
|
|
6
8
|
|
|
7
9
|
export type CreateDatasetParams = ClientFn & {
|
|
8
10
|
/**
|
|
@@ -24,7 +26,10 @@ export type CreateDatasetResponse = {
|
|
|
24
26
|
};
|
|
25
27
|
|
|
26
28
|
/**
|
|
27
|
-
* Create a
|
|
29
|
+
* Create a dataset with the given examples.
|
|
30
|
+
*
|
|
31
|
+
* If a dataset with the same name already exists, it is updated to match the
|
|
32
|
+
* provided examples. Re-running with the same inputs is a no-op.
|
|
28
33
|
*
|
|
29
34
|
* @experimental this interface may change in the future
|
|
30
35
|
*
|
|
@@ -82,27 +87,70 @@ export async function createDataset({
|
|
|
82
87
|
// Only include span_ids in the request if at least one example has a span ID
|
|
83
88
|
const hasSpanIds = spanIds.some((id) => id !== null);
|
|
84
89
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
+
// Extract example IDs from examples, preserving null/undefined as null
|
|
91
|
+
const exampleIds = examples.map((example) => example?.id ?? null);
|
|
92
|
+
|
|
93
|
+
// Only include example_ids in the request if at least one example has an ID
|
|
94
|
+
const hasExampleIds = exampleIds.some((id) => id !== null);
|
|
95
|
+
|
|
96
|
+
const post = (action: "update" | "create") =>
|
|
97
|
+
client.POST("/v1/datasets/upload", {
|
|
98
|
+
params: {
|
|
99
|
+
query: {
|
|
100
|
+
// TODO: parameterize this
|
|
101
|
+
sync: true,
|
|
102
|
+
},
|
|
90
103
|
},
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
104
|
+
body: {
|
|
105
|
+
name,
|
|
106
|
+
description,
|
|
107
|
+
action,
|
|
108
|
+
inputs,
|
|
109
|
+
outputs,
|
|
110
|
+
metadata,
|
|
111
|
+
splits,
|
|
112
|
+
...(hasSpanIds ? { span_ids: spanIds } : {}),
|
|
113
|
+
...(hasExampleIds ? { example_ids: exampleIds } : {}),
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
if (hasExampleIds) {
|
|
118
|
+
await ensureServerCapability({
|
|
119
|
+
client,
|
|
120
|
+
requirement: DATASET_UPLOAD_EXAMPLE_IDS,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
let createDatasetResponse = await post("update");
|
|
124
|
+
if (isUnsupportedUpdateActionResponse(createDatasetResponse)) {
|
|
125
|
+
warnUpdateFallback();
|
|
126
|
+
createDatasetResponse = await post("create");
|
|
127
|
+
}
|
|
103
128
|
invariant(createDatasetResponse.data?.data, "Failed to create dataset");
|
|
104
129
|
const datasetId = createDatasetResponse.data.data.dataset_id;
|
|
105
130
|
return {
|
|
106
131
|
datasetId,
|
|
107
132
|
};
|
|
108
133
|
}
|
|
134
|
+
|
|
135
|
+
function isUnsupportedUpdateActionResponse(result: {
|
|
136
|
+
response?: Response;
|
|
137
|
+
error?: unknown;
|
|
138
|
+
}): boolean {
|
|
139
|
+
if (result.response?.status !== 422) return false;
|
|
140
|
+
const body =
|
|
141
|
+
typeof result.error === "string"
|
|
142
|
+
? result.error
|
|
143
|
+
: JSON.stringify(result.error ?? "");
|
|
144
|
+
return (
|
|
145
|
+
body.includes("Invalid dateset action") ||
|
|
146
|
+
body.includes("Invalid dataset action")
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function warnUpdateFallback(): void {
|
|
151
|
+
// eslint-disable-next-line no-console
|
|
152
|
+
console.warn(
|
|
153
|
+
"Phoenix server does not support declarative update semantics. " +
|
|
154
|
+
"Upgrade to Phoenix v15 or later."
|
|
155
|
+
);
|
|
156
|
+
}
|
|
@@ -51,9 +51,12 @@ export async function getDatasetExamples({
|
|
|
51
51
|
const examplesData = response.data.data;
|
|
52
52
|
return {
|
|
53
53
|
versionId: examplesData.version_id,
|
|
54
|
-
examples: examplesData.examples.map(
|
|
55
|
-
...example
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
examples: examplesData.examples.map(
|
|
55
|
+
({ node_id, updated_at, ...example }) => ({
|
|
56
|
+
...example,
|
|
57
|
+
nodeId: node_id,
|
|
58
|
+
updatedAt: new Date(updated_at),
|
|
59
|
+
})
|
|
60
|
+
),
|
|
58
61
|
};
|
|
59
62
|
}
|
package/src/datasets/index.ts
CHANGED
|
@@ -136,6 +136,7 @@ function buildIncompleteEvaluation(
|
|
|
136
136
|
},
|
|
137
137
|
datasetExample: {
|
|
138
138
|
id: apiResponse.dataset_example.id,
|
|
139
|
+
nodeId: apiResponse.dataset_example.node_id,
|
|
139
140
|
input: apiResponse.dataset_example.input,
|
|
140
141
|
output: apiResponse.dataset_example.output ?? null,
|
|
141
142
|
metadata: apiResponse.dataset_example.metadata || {},
|
|
@@ -728,7 +729,7 @@ async function runSingleEvaluation({
|
|
|
728
729
|
...objectAsAttributes({
|
|
729
730
|
experiment_id: experimentId,
|
|
730
731
|
experiment_run_id: experimentRun.id,
|
|
731
|
-
dataset_example_id: datasetExample.
|
|
732
|
+
dataset_example_id: datasetExample.nodeId,
|
|
732
733
|
}),
|
|
733
734
|
});
|
|
734
735
|
|
|
@@ -132,6 +132,7 @@ function buildExampleFromApiResponse(
|
|
|
132
132
|
): ExampleWithId {
|
|
133
133
|
return {
|
|
134
134
|
id: apiExample.id,
|
|
135
|
+
nodeId: apiExample.node_id,
|
|
135
136
|
input: apiExample.input,
|
|
136
137
|
output: apiExample.output || null,
|
|
137
138
|
metadata: apiExample.metadata || {},
|
|
@@ -618,7 +619,7 @@ async function recordTaskResult({
|
|
|
618
619
|
},
|
|
619
620
|
},
|
|
620
621
|
body: {
|
|
621
|
-
dataset_example_id: example.
|
|
622
|
+
dataset_example_id: example.nodeId,
|
|
622
623
|
repetition_number: repetitionNumber,
|
|
623
624
|
output: output as Record<string, unknown>,
|
|
624
625
|
start_time: startTime.toISOString(),
|
|
@@ -694,7 +695,7 @@ async function runSingleTask({
|
|
|
694
695
|
[SemanticConventions.INPUT_MIME_TYPE]: MimeType.JSON,
|
|
695
696
|
...objectAsAttributes({
|
|
696
697
|
experiment_id: experimentId,
|
|
697
|
-
dataset_example_id: example.
|
|
698
|
+
dataset_example_id: example.nodeId,
|
|
698
699
|
repetition_number: repetitionNumber,
|
|
699
700
|
}),
|
|
700
701
|
});
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { createClient } from "../client";
|
|
2
|
+
import { ADD_SESSION_NOTE } from "../constants/serverRequirements";
|
|
3
|
+
import type { ClientFn } from "../types/core";
|
|
4
|
+
import { formatApiError } from "../utils/apiErrorUtils";
|
|
5
|
+
import { ensureServerCapability } from "../utils/serverVersionUtils";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Parameters for a single session note.
|
|
9
|
+
*/
|
|
10
|
+
export interface SessionNote {
|
|
11
|
+
/**
|
|
12
|
+
* The session ID used to track a conversation, thread, or session.
|
|
13
|
+
*/
|
|
14
|
+
sessionId: string;
|
|
15
|
+
/**
|
|
16
|
+
* The note text to add to the session.
|
|
17
|
+
*/
|
|
18
|
+
note: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Parameters to add a session note.
|
|
23
|
+
*/
|
|
24
|
+
export interface AddSessionNoteParams extends ClientFn {
|
|
25
|
+
sessionNote: SessionNote;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Add a note to a session.
|
|
30
|
+
*
|
|
31
|
+
* Notes are a special type of annotation that allow multiple entries per session.
|
|
32
|
+
* Each note gets a unique UUIDv4 identifier.
|
|
33
|
+
*
|
|
34
|
+
* @param params - The parameters to add a session note.
|
|
35
|
+
* @returns The ID of the created note annotation.
|
|
36
|
+
*
|
|
37
|
+
* @requires Phoenix server >= 14.17.0
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```ts
|
|
41
|
+
* const result = await addSessionNote({
|
|
42
|
+
* sessionNote: {
|
|
43
|
+
* sessionId: "my-session",
|
|
44
|
+
* note: "Needs review"
|
|
45
|
+
* }
|
|
46
|
+
* });
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export async function addSessionNote({
|
|
50
|
+
client: _client,
|
|
51
|
+
sessionNote,
|
|
52
|
+
}: AddSessionNoteParams): Promise<{ id: string }> {
|
|
53
|
+
const client = _client ?? createClient();
|
|
54
|
+
await ensureServerCapability({ client, requirement: ADD_SESSION_NOTE });
|
|
55
|
+
|
|
56
|
+
const { data, error } = await client.POST("/v1/session_notes", {
|
|
57
|
+
body: {
|
|
58
|
+
data: {
|
|
59
|
+
session_id: sessionNote.sessionId.trim(),
|
|
60
|
+
note: sessionNote.note,
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
if (error) {
|
|
66
|
+
throw new Error(`Failed to add session note: ${formatApiError(error)}`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (!data?.data) {
|
|
70
|
+
throw new Error("Failed to add session note: no data returned");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return data.data;
|
|
74
|
+
}
|
package/src/sessions/index.ts
CHANGED
package/src/spans/addSpanNote.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createClient } from "../client";
|
|
2
2
|
import type { ClientFn } from "../types/core";
|
|
3
|
+
import { formatApiError } from "../utils/apiErrorUtils";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Parameters for a single span note
|
|
@@ -25,9 +26,11 @@ export interface AddSpanNoteParams extends ClientFn {
|
|
|
25
26
|
/**
|
|
26
27
|
* Add a note to a span.
|
|
27
28
|
*
|
|
28
|
-
* Notes are
|
|
29
|
-
*
|
|
30
|
-
*
|
|
29
|
+
* Notes are append-only: each call creates a new note with an auto-generated
|
|
30
|
+
* UUIDv4 identifier, so multiple notes accumulate on the same span. Structured
|
|
31
|
+
* annotations, by contrast, are keyed by `(name, spanId, identifier)` — to keep
|
|
32
|
+
* multiple structured annotations with the same name on a span, supply distinct
|
|
33
|
+
* identifiers; otherwise re-writing the same name overwrites the existing one.
|
|
31
34
|
*
|
|
32
35
|
* @param params - The parameters to add a span note
|
|
33
36
|
* @returns The ID of the created note annotation
|
|
@@ -58,7 +61,7 @@ export async function addSpanNote({
|
|
|
58
61
|
});
|
|
59
62
|
|
|
60
63
|
if (error) {
|
|
61
|
-
throw new Error(`Failed to add span note: ${error}`);
|
|
64
|
+
throw new Error(`Failed to add span note: ${formatApiError(error)}`);
|
|
62
65
|
}
|
|
63
66
|
|
|
64
67
|
if (!data?.data) {
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { createClient } from "../client";
|
|
2
|
+
import type { ClientFn } from "../types/core";
|
|
3
|
+
import type { TraceAnnotation } from "./types";
|
|
4
|
+
import { toTraceAnnotationData } from "./types";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Parameters to add a trace annotation
|
|
8
|
+
*/
|
|
9
|
+
export interface AddTraceAnnotationParams extends ClientFn {
|
|
10
|
+
traceAnnotation: TraceAnnotation;
|
|
11
|
+
/**
|
|
12
|
+
* If true, the request will be fulfilled synchronously and return the annotation ID.
|
|
13
|
+
* If false, the request will be processed asynchronously and return null.
|
|
14
|
+
* @default false
|
|
15
|
+
*/
|
|
16
|
+
sync?: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Add an annotation to a trace.
|
|
21
|
+
*
|
|
22
|
+
* The annotation can be of type "LLM", "CODE", or "HUMAN" and can include a label, score, and metadata.
|
|
23
|
+
* If an identifier is provided and an annotation with that identifier already exists, it will be updated.
|
|
24
|
+
*
|
|
25
|
+
* @param params - The parameters to add a trace annotation
|
|
26
|
+
* @returns The ID of the created or updated annotation
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* const result = await addTraceAnnotation({
|
|
31
|
+
* traceAnnotation: {
|
|
32
|
+
* traceId: "abc123",
|
|
33
|
+
* name: "correctness",
|
|
34
|
+
* label: "correct",
|
|
35
|
+
* score: 1.0,
|
|
36
|
+
* annotatorKind: "HUMAN",
|
|
37
|
+
* identifier: "custom_id_123",
|
|
38
|
+
* metadata: { reviewer: "alice" }
|
|
39
|
+
* },
|
|
40
|
+
* sync: true,
|
|
41
|
+
* });
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export async function addTraceAnnotation({
|
|
45
|
+
client: _client,
|
|
46
|
+
traceAnnotation,
|
|
47
|
+
sync = false,
|
|
48
|
+
}: AddTraceAnnotationParams): Promise<{ id: string } | null> {
|
|
49
|
+
const client = _client ?? createClient();
|
|
50
|
+
|
|
51
|
+
const { data, error } = await client.POST("/v1/trace_annotations", {
|
|
52
|
+
params: {
|
|
53
|
+
query: { sync },
|
|
54
|
+
},
|
|
55
|
+
body: {
|
|
56
|
+
data: [toTraceAnnotationData(traceAnnotation)],
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
if (error) {
|
|
61
|
+
throw new Error(`Failed to add trace annotation: ${error}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return data?.data?.[0] || null;
|
|
65
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createClient } from "../client";
|
|
2
2
|
import { ADD_TRACE_NOTE } from "../constants/serverRequirements";
|
|
3
3
|
import type { ClientFn } from "../types/core";
|
|
4
|
+
import { formatApiError } from "../utils/apiErrorUtils";
|
|
4
5
|
import { ensureServerCapability } from "../utils/serverVersionUtils";
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -27,8 +28,11 @@ export interface AddTraceNoteParams extends ClientFn {
|
|
|
27
28
|
/**
|
|
28
29
|
* Add a note to a trace.
|
|
29
30
|
*
|
|
30
|
-
* Notes are
|
|
31
|
-
*
|
|
31
|
+
* Notes are append-only: each call creates a new note with an auto-generated
|
|
32
|
+
* UUIDv4 identifier, so multiple notes accumulate on the same trace. Structured
|
|
33
|
+
* annotations, by contrast, are keyed by `(name, traceId, identifier)` — to keep
|
|
34
|
+
* multiple structured annotations with the same name on a trace, supply distinct
|
|
35
|
+
* identifiers; otherwise re-writing the same name overwrites the existing one.
|
|
32
36
|
*
|
|
33
37
|
* @param params - The parameters to add a trace note.
|
|
34
38
|
* @returns The ID of the created note annotation.
|
|
@@ -60,7 +64,7 @@ export async function addTraceNote({
|
|
|
60
64
|
});
|
|
61
65
|
|
|
62
66
|
if (error) {
|
|
63
|
-
throw new Error(`Failed to add trace note: ${error}`);
|
|
67
|
+
throw new Error(`Failed to add trace note: ${formatApiError(error)}`);
|
|
64
68
|
}
|
|
65
69
|
|
|
66
70
|
if (!data?.data) {
|
package/src/traces/index.ts
CHANGED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { createClient } from "../client";
|
|
2
|
+
import type { ClientFn } from "../types/core";
|
|
3
|
+
import type { TraceAnnotation } from "./types";
|
|
4
|
+
import { toTraceAnnotationData } from "./types";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Parameters to log multiple trace annotations
|
|
8
|
+
*/
|
|
9
|
+
export interface LogTraceAnnotationsParams extends ClientFn {
|
|
10
|
+
/**
|
|
11
|
+
* The trace annotations to log
|
|
12
|
+
*/
|
|
13
|
+
traceAnnotations: TraceAnnotation[];
|
|
14
|
+
/**
|
|
15
|
+
* If true, the request will be fulfilled synchronously and return the annotation IDs.
|
|
16
|
+
* If false, the request will be processed asynchronously and return null.
|
|
17
|
+
* @default false
|
|
18
|
+
*/
|
|
19
|
+
sync?: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Log multiple trace annotations in a single request.
|
|
24
|
+
*
|
|
25
|
+
* Each annotation can be of type "LLM", "CODE", or "HUMAN" and can include a label, score, and metadata.
|
|
26
|
+
* If an identifier is provided and an annotation with that identifier already exists, it will be updated.
|
|
27
|
+
*
|
|
28
|
+
* @param params - The parameters to log trace annotations
|
|
29
|
+
* @returns The IDs of the created or updated annotations
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```ts
|
|
33
|
+
* const results = await logTraceAnnotations({
|
|
34
|
+
* traceAnnotations: [
|
|
35
|
+
* {
|
|
36
|
+
* traceId: "abc123",
|
|
37
|
+
* name: "correctness",
|
|
38
|
+
* label: "correct",
|
|
39
|
+
* score: 1.0,
|
|
40
|
+
* annotatorKind: "HUMAN",
|
|
41
|
+
* },
|
|
42
|
+
* {
|
|
43
|
+
* traceId: "def456",
|
|
44
|
+
* name: "faithfulness",
|
|
45
|
+
* label: "faithful",
|
|
46
|
+
* score: 0.9,
|
|
47
|
+
* annotatorKind: "LLM",
|
|
48
|
+
* },
|
|
49
|
+
* ],
|
|
50
|
+
* sync: true,
|
|
51
|
+
* });
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export async function logTraceAnnotations({
|
|
55
|
+
client: _client,
|
|
56
|
+
traceAnnotations,
|
|
57
|
+
sync = false,
|
|
58
|
+
}: LogTraceAnnotationsParams): Promise<{ id: string }[]> {
|
|
59
|
+
const client = _client ?? createClient();
|
|
60
|
+
|
|
61
|
+
const { data, error } = await client.POST("/v1/trace_annotations", {
|
|
62
|
+
params: {
|
|
63
|
+
query: { sync },
|
|
64
|
+
},
|
|
65
|
+
body: {
|
|
66
|
+
data: traceAnnotations.map(toTraceAnnotationData),
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
if (error) {
|
|
71
|
+
throw new Error(`Failed to log trace annotations: ${error}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return data?.data || [];
|
|
75
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { paths } from "../__generated__/api/v1";
|
|
2
|
+
import type { Annotation, AnnotationResult } from "../types/annotations";
|
|
3
|
+
|
|
4
|
+
type TraceAnnotationData =
|
|
5
|
+
paths["/v1/trace_annotations"]["post"]["requestBody"]["content"]["application/json"]["data"][0];
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Parameters for a single trace annotation
|
|
9
|
+
*/
|
|
10
|
+
export interface TraceAnnotation extends Annotation {
|
|
11
|
+
/**
|
|
12
|
+
* The OpenTelemetry Trace ID (hex format without 0x prefix)
|
|
13
|
+
*/
|
|
14
|
+
traceId: string;
|
|
15
|
+
/**
|
|
16
|
+
* The kind of annotator used for the annotation
|
|
17
|
+
* Can be "HUMAN", "LLM", or "CODE"
|
|
18
|
+
* @default "HUMAN"
|
|
19
|
+
*/
|
|
20
|
+
annotatorKind?: TraceAnnotationData["annotator_kind"];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Build and validate annotation result fields
|
|
25
|
+
*/
|
|
26
|
+
function buildTraceAnnotationResult(
|
|
27
|
+
annotation: Pick<TraceAnnotation, "label" | "score" | "explanation">
|
|
28
|
+
): AnnotationResult {
|
|
29
|
+
const result: AnnotationResult = {};
|
|
30
|
+
|
|
31
|
+
if (annotation.label !== undefined) {
|
|
32
|
+
result.label = annotation.label.trim() || null;
|
|
33
|
+
}
|
|
34
|
+
if (annotation.score !== undefined) {
|
|
35
|
+
result.score = annotation.score;
|
|
36
|
+
}
|
|
37
|
+
if (annotation.explanation !== undefined) {
|
|
38
|
+
result.explanation = annotation.explanation.trim() || null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const hasValidResult =
|
|
42
|
+
result.label || result.score !== undefined || result.explanation;
|
|
43
|
+
if (!hasValidResult) {
|
|
44
|
+
throw new Error(
|
|
45
|
+
"At least one of label, score, or explanation must be provided for trace annotation"
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Convert a TraceAnnotation to the API format
|
|
53
|
+
*/
|
|
54
|
+
export function toTraceAnnotationData(
|
|
55
|
+
annotation: TraceAnnotation
|
|
56
|
+
): TraceAnnotationData {
|
|
57
|
+
if (annotation.name.trim() === "note") {
|
|
58
|
+
throw new Error(
|
|
59
|
+
'The name "note" is reserved for trace and span notes. Use addTraceNote instead.'
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
const result = buildTraceAnnotationResult(annotation);
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
trace_id: annotation.traceId.trim(),
|
|
66
|
+
name: annotation.name.trim(),
|
|
67
|
+
annotator_kind: annotation.annotatorKind ?? "HUMAN",
|
|
68
|
+
result,
|
|
69
|
+
metadata: annotation.metadata ?? null,
|
|
70
|
+
identifier: annotation.identifier?.trim() ?? "",
|
|
71
|
+
};
|
|
72
|
+
}
|
package/src/types/datasets.ts
CHANGED
|
@@ -58,12 +58,21 @@ export interface Example {
|
|
|
58
58
|
* in the Phoenix UI, enabling traceability from datasets back to traces.
|
|
59
59
|
*/
|
|
60
60
|
spanId?: string | null;
|
|
61
|
+
/**
|
|
62
|
+
* Optional user-provided ID for this example.
|
|
63
|
+
* If not provided, the server will generate an ID.
|
|
64
|
+
*/
|
|
65
|
+
id?: string | null;
|
|
61
66
|
}
|
|
62
67
|
|
|
63
68
|
/**
|
|
64
69
|
* An example that has been synced to the server
|
|
65
70
|
*/
|
|
66
|
-
export interface ExampleWithId extends Example
|
|
71
|
+
export interface ExampleWithId extends Example {
|
|
72
|
+
/** The user-provided or server-generated ID for this example. */
|
|
73
|
+
id: string;
|
|
74
|
+
/** Server-generated node ID for this example. */
|
|
75
|
+
nodeId: string;
|
|
67
76
|
updatedAt: Date;
|
|
68
77
|
}
|
|
69
78
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function formatApiError(error: unknown): string {
|
|
2
|
+
if (typeof error === "string") {
|
|
3
|
+
return error;
|
|
4
|
+
}
|
|
5
|
+
if (error && typeof error === "object") {
|
|
6
|
+
const errorWithDetail = error as { detail?: unknown };
|
|
7
|
+
if (typeof errorWithDetail.detail === "string") {
|
|
8
|
+
return errorWithDetail.detail;
|
|
9
|
+
}
|
|
10
|
+
try {
|
|
11
|
+
return JSON.stringify(error);
|
|
12
|
+
} catch {
|
|
13
|
+
return String(error);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return String(error);
|
|
17
|
+
}
|