@arizeai/phoenix-client 7.12.0 → 7.14.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.
Files changed (88) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +27 -0
  3. package/dist/esm/__generated__/api/v1.d.ts +3 -3
  4. package/dist/esm/constants/serverRequirements.d.ts +3 -0
  5. package/dist/esm/constants/serverRequirements.d.ts.map +1 -1
  6. package/dist/esm/constants/serverRequirements.js +21 -0
  7. package/dist/esm/constants/serverRequirements.js.map +1 -1
  8. package/dist/esm/datasets/createDatasetSplit.d.ts +39 -0
  9. package/dist/esm/datasets/createDatasetSplit.d.ts.map +1 -0
  10. package/dist/esm/datasets/createDatasetSplit.js +42 -0
  11. package/dist/esm/datasets/createDatasetSplit.js.map +1 -0
  12. package/dist/esm/datasets/deleteDatasetSplit.d.ts +24 -0
  13. package/dist/esm/datasets/deleteDatasetSplit.d.ts.map +1 -0
  14. package/dist/esm/datasets/deleteDatasetSplit.js +31 -0
  15. package/dist/esm/datasets/deleteDatasetSplit.js.map +1 -0
  16. package/dist/esm/datasets/index.d.ts +4 -0
  17. package/dist/esm/datasets/index.d.ts.map +1 -1
  18. package/dist/esm/datasets/index.js +3 -0
  19. package/dist/esm/datasets/index.js.map +1 -1
  20. package/dist/esm/datasets/resolveDatasetIdentifier.d.ts +4 -0
  21. package/dist/esm/datasets/resolveDatasetIdentifier.d.ts.map +1 -0
  22. package/dist/esm/datasets/resolveDatasetIdentifier.js +5 -0
  23. package/dist/esm/datasets/resolveDatasetIdentifier.js.map +1 -0
  24. package/dist/esm/datasets/updateDatasetSplit.d.ts +48 -0
  25. package/dist/esm/datasets/updateDatasetSplit.d.ts.map +1 -0
  26. package/dist/esm/datasets/updateDatasetSplit.js +55 -0
  27. package/dist/esm/datasets/updateDatasetSplit.js.map +1 -0
  28. package/dist/esm/secrets/index.d.ts +2 -0
  29. package/dist/esm/secrets/index.d.ts.map +1 -0
  30. package/dist/esm/secrets/index.js +2 -0
  31. package/dist/esm/secrets/index.js.map +1 -0
  32. package/dist/esm/secrets/upsertOrDeleteSecrets.d.ts +60 -0
  33. package/dist/esm/secrets/upsertOrDeleteSecrets.d.ts.map +1 -0
  34. package/dist/esm/secrets/upsertOrDeleteSecrets.js +47 -0
  35. package/dist/esm/secrets/upsertOrDeleteSecrets.js.map +1 -0
  36. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  37. package/dist/esm/types/datasets.d.ts +10 -5
  38. package/dist/esm/types/datasets.d.ts.map +1 -1
  39. package/dist/src/__generated__/api/v1.d.ts +3 -3
  40. package/dist/src/constants/serverRequirements.d.ts +3 -0
  41. package/dist/src/constants/serverRequirements.d.ts.map +1 -1
  42. package/dist/src/constants/serverRequirements.js +22 -1
  43. package/dist/src/constants/serverRequirements.js.map +1 -1
  44. package/dist/src/datasets/createDatasetSplit.d.ts +39 -0
  45. package/dist/src/datasets/createDatasetSplit.d.ts.map +1 -0
  46. package/dist/src/datasets/createDatasetSplit.js +42 -0
  47. package/dist/src/datasets/createDatasetSplit.js.map +1 -0
  48. package/dist/src/datasets/deleteDatasetSplit.d.ts +24 -0
  49. package/dist/src/datasets/deleteDatasetSplit.d.ts.map +1 -0
  50. package/dist/src/datasets/deleteDatasetSplit.js +34 -0
  51. package/dist/src/datasets/deleteDatasetSplit.js.map +1 -0
  52. package/dist/src/datasets/index.d.ts +4 -0
  53. package/dist/src/datasets/index.d.ts.map +1 -1
  54. package/dist/src/datasets/index.js +3 -0
  55. package/dist/src/datasets/index.js.map +1 -1
  56. package/dist/src/datasets/resolveDatasetIdentifier.d.ts +4 -0
  57. package/dist/src/datasets/resolveDatasetIdentifier.d.ts.map +1 -0
  58. package/dist/src/datasets/resolveDatasetIdentifier.js +8 -0
  59. package/dist/src/datasets/resolveDatasetIdentifier.js.map +1 -0
  60. package/dist/src/datasets/updateDatasetSplit.d.ts +48 -0
  61. package/dist/src/datasets/updateDatasetSplit.d.ts.map +1 -0
  62. package/dist/src/datasets/updateDatasetSplit.js +54 -0
  63. package/dist/src/datasets/updateDatasetSplit.js.map +1 -0
  64. package/dist/src/secrets/index.d.ts +2 -0
  65. package/dist/src/secrets/index.d.ts.map +1 -0
  66. package/dist/src/secrets/index.js +18 -0
  67. package/dist/src/secrets/index.js.map +1 -0
  68. package/dist/src/secrets/upsertOrDeleteSecrets.d.ts +60 -0
  69. package/dist/src/secrets/upsertOrDeleteSecrets.d.ts.map +1 -0
  70. package/dist/src/secrets/upsertOrDeleteSecrets.js +50 -0
  71. package/dist/src/secrets/upsertOrDeleteSecrets.js.map +1 -0
  72. package/dist/src/types/datasets.d.ts +10 -5
  73. package/dist/src/types/datasets.d.ts.map +1 -1
  74. package/dist/tsconfig.tsbuildinfo +1 -1
  75. package/docs/datasets.mdx +74 -1
  76. package/docs/overview.mdx +4 -1
  77. package/docs/secrets.mdx +68 -0
  78. package/package.json +6 -2
  79. package/src/__generated__/api/v1.ts +3 -3
  80. package/src/constants/serverRequirements.ts +24 -0
  81. package/src/datasets/createDatasetSplit.ts +80 -0
  82. package/src/datasets/deleteDatasetSplit.ts +46 -0
  83. package/src/datasets/index.ts +4 -0
  84. package/src/datasets/resolveDatasetIdentifier.ts +6 -0
  85. package/src/datasets/updateDatasetSplit.ts +99 -0
  86. package/src/secrets/index.ts +1 -0
  87. package/src/secrets/upsertOrDeleteSecrets.ts +87 -0
  88. package/src/types/datasets.ts +8 -3
package/docs/datasets.mdx CHANGED
@@ -3,7 +3,7 @@ title: "Datasets"
3
3
  description: "Create and inspect datasets with @arizeai/phoenix-client"
4
4
  ---
5
5
 
6
- Datasets are the foundation for experiment runs. The dataset helpers cover creation (which upserts by name), record inspection, and example appends.
6
+ Datasets are the foundation for experiment runs. The dataset helpers cover creation (which upserts by name), record inspection, example appends, and split management.
7
7
 
8
8
  <section className="hidden" data-agent-context="relevant-source-files" aria-label="Relevant source files">
9
9
  <h2>Relevant Source Files</h2>
@@ -25,14 +25,27 @@ const { datasetId } = await createDataset({
25
25
  description: "Support questions with expected answers",
26
26
  examples: [
27
27
  {
28
+ id: "order-tracking",
28
29
  input: { question: "Where is my order?" },
29
30
  output: { answer: "Use the tracking page in your account." },
30
31
  metadata: { channel: "chat" },
31
32
  },
33
+ {
34
+ id: "password-reset",
35
+ input: { question: "How do I reset my password?" },
36
+ output: { answer: "Use the forgot password flow." },
37
+ metadata: { channel: "chat" },
38
+ },
32
39
  ],
33
40
  });
34
41
  ```
35
42
 
43
+ `id` is optional. When you do provide it, give every example its own value:
44
+ example IDs are unique per dataset, so reusing one across examples in the same
45
+ dataset is an error. A stable, unique `id` is what `createDataset()` matches on
46
+ when it upserts, and what you can hand to the split helpers below instead of the
47
+ server-generated `nodeId`. Omit `id` and the server generates one for you.
48
+
36
49
  ## Upsert Or Append
37
50
 
38
51
  `createDataset()` upserts by name: re-running it with the same name updates the existing dataset to match the examples you pass, and an unchanged upload is a no-op. To keep existing examples and add more, use `appendDatasetExamples()` instead of re-running `createDataset()` with the extra examples.
@@ -48,6 +61,7 @@ const dataset = await createDataset({
48
61
  description: "Support questions with expected answers",
49
62
  examples: [
50
63
  {
64
+ id: "order-tracking",
51
65
  input: { question: "Where is my order?" },
52
66
  output: { answer: "Use the tracking page in your account." },
53
67
  },
@@ -58,6 +72,7 @@ await appendDatasetExamples({
58
72
  dataset,
59
73
  examples: [
60
74
  {
75
+ id: "password-reset",
61
76
  input: { question: "How do I reset my password?" },
62
77
  output: { answer: "Use the forgot password flow." },
63
78
  },
@@ -71,6 +86,61 @@ await appendDatasetExamples({
71
86
 
72
87
  Use `getDataset`, `getDatasetExamples`, and `getDatasetInfo` to inspect datasets after creation.
73
88
 
89
+ ## Manage Splits On An Existing Dataset
90
+
91
+ Use `createDatasetSplit`, `updateDatasetSplit`, and `deleteDatasetSplit` to
92
+ manage train, test, validation, or other named subsets after a dataset exists.
93
+ Select the dataset by name or GlobalID. Example membership accepts either the
94
+ user-provided `id` or Phoenix `nodeId` returned by `getDatasetExamples`.
95
+
96
+ ```ts
97
+ import {
98
+ createDatasetSplit,
99
+ deleteDatasetSplit,
100
+ getDatasetExamples,
101
+ updateDatasetSplit,
102
+ } from "@arizeai/phoenix-client/datasets";
103
+
104
+ const datasetIdentifier = "support-eval";
105
+ const dataset = { datasetName: datasetIdentifier };
106
+ const { examples } = await getDatasetExamples({
107
+ dataset,
108
+ });
109
+ const [firstExample, secondExample] = examples;
110
+ if (firstExample == null || secondExample == null) {
111
+ throw new Error("At least two examples are required to demonstrate split updates");
112
+ }
113
+
114
+ const testSplit = await createDatasetSplit({
115
+ dataset,
116
+ name: "test",
117
+ description: "Held-out evaluation examples",
118
+ exampleIds: [firstExample.id],
119
+ });
120
+
121
+ // Only provided fields change. Adding an existing member or removing a
122
+ // non-member is an idempotent no-op.
123
+ await updateDatasetSplit({
124
+ dataset,
125
+ splitId: testSplit.id,
126
+ description: "Reviewed held-out examples",
127
+ addExampleIds: [secondExample.id],
128
+ removeExampleIds: [firstExample.id],
129
+ });
130
+
131
+ // Deleting a split removes its memberships, not the underlying examples.
132
+ await deleteDatasetSplit({
133
+ dataset,
134
+ splitId: testSplit.id,
135
+ });
136
+ ```
137
+
138
+ Split names are unique across the Phoenix instance. Creating or renaming a
139
+ split to an existing name fails with HTTP 409. These helpers require Phoenix
140
+ server 19.20.0 or newer. Servers on 19.20.0 through 20.15.0 resolve membership
141
+ by `nodeId` only — user-provided `id` values are accepted from the release that
142
+ follows 20.15.0, so pass `nodeId` when you target an older server.
143
+
74
144
  <section className="hidden" data-agent-context="source-map" aria-label="Source map">
75
145
  <h2>Source Map</h2>
76
146
  <ul>
@@ -79,5 +149,8 @@ Use `getDataset`, `getDatasetExamples`, and `getDatasetInfo` to inspect datasets
79
149
  <li><code>src/datasets/getDataset.ts</code></li>
80
150
  <li><code>src/datasets/getDatasetExamples.ts</code></li>
81
151
  <li><code>src/datasets/getDatasetInfo.ts</code></li>
152
+ <li><code>src/datasets/createDatasetSplit.ts</code></li>
153
+ <li><code>src/datasets/updateDatasetSplit.ts</code></li>
154
+ <li><code>src/datasets/deleteDatasetSplit.ts</code></li>
82
155
  </ul>
83
156
  </section>
package/docs/overview.mdx CHANGED
@@ -3,7 +3,7 @@ title: "Overview"
3
3
  description: "Typed TypeScript client for Phoenix platform APIs"
4
4
  ---
5
5
 
6
- `@arizeai/phoenix-client` is the typed TypeScript client for Phoenix platform APIs. It ships a small root REST client plus focused module entrypoints for projects, prompts, datasets, experiments, spans, sessions, traces, users, and CI-friendly dataset-backed eval tests.
6
+ `@arizeai/phoenix-client` is the typed TypeScript client for Phoenix platform APIs. It ships a small root REST client plus focused module entrypoints for projects, prompts, datasets, experiments, spans, sessions, traces, secrets, users, and CI-friendly dataset-backed eval tests.
7
7
 
8
8
  ## Install
9
9
 
@@ -44,6 +44,7 @@ That gives the agent version-matched docs plus the exact implementation and gene
44
44
  | `@arizeai/phoenix-client/spans` | Span search, notes, and span/document annotations |
45
45
  | `@arizeai/phoenix-client/sessions` | Session listing, retrieval, and session annotations |
46
46
  | `@arizeai/phoenix-client/traces` | Project trace retrieval, transfers, and trace annotations |
47
+ | `@arizeai/phoenix-client/secrets` | Atomic secret creation, rotation, and deletion |
47
48
  | `@arizeai/phoenix-client/users` | Current authenticated user retrieval |
48
49
  | `@arizeai/phoenix-client/vitest` | Vitest entrypoint for dataset-backed eval tests |
49
50
  | `@arizeai/phoenix-client/vitest/reporter` | Vitest reporter for Phoenix eval summaries |
@@ -164,6 +165,7 @@ Prefer this layer when:
164
165
  - [Annotations](./annotations) — annotation concepts, then [Span](./span-annotations), [Document](./document-annotations), and [Session](./session-annotations) annotations for detailed usage
165
166
  - [CI Eval Tests](./ci-evals) — Vitest/Jest eval suites backed by Phoenix datasets and experiments
166
167
  - [Spans](./spans), [Sessions](./sessions), [Traces](./traces), [Users](./users) — retrieval and maintenance
168
+ - [Secrets](./secrets) — encrypted provider credential management
167
169
 
168
170
  <section className="hidden" data-agent-context="source-map" aria-label="Source map">
169
171
  <h2>Source Map</h2>
@@ -180,6 +182,7 @@ Prefer this layer when:
180
182
  <li><code>src/spans/</code></li>
181
183
  <li><code>src/sessions/</code></li>
182
184
  <li><code>src/traces/</code></li>
185
+ <li><code>src/secrets/</code></li>
183
186
  <li><code>src/users/</code></li>
184
187
  <li><code>src/vitest/</code></li>
185
188
  <li><code>src/jest/</code></li>
@@ -0,0 +1,68 @@
1
+ ---
2
+ title: "Secrets"
3
+ description: "Atomically manage encrypted provider credentials with the Phoenix TypeScript client"
4
+ ---
5
+
6
+ Use `upsertOrDeleteSecrets` from the `@arizeai/phoenix-client/secrets` entrypoint to create, rotate, or delete encrypted provider credentials in one atomic request.
7
+
8
+ <Warning>
9
+ Managing secrets requires an administrator when Phoenix authentication is enabled. Do not log the request batch or retain its values outside your credential store.
10
+ </Warning>
11
+
12
+ ## Create, Update, And Delete Secrets
13
+
14
+ Each batch entry has a `key` and a required `value`:
15
+
16
+ - A string value creates or updates the secret.
17
+ - `null` deletes the secret.
18
+ - When a key occurs more than once, its last occurrence wins.
19
+
20
+ ```ts
21
+ import { upsertOrDeleteSecrets } from "@arizeai/phoenix-client/secrets";
22
+
23
+ const apiKey = process.env.OPENAI_API_KEY;
24
+ if (!apiKey) throw new Error("OPENAI_API_KEY is required");
25
+
26
+ const result = await upsertOrDeleteSecrets({
27
+ secrets: [
28
+ { key: "OPENAI_API_KEY", value: apiKey },
29
+ { key: "OLD_PROVIDER_API_KEY", value: null },
30
+ ],
31
+ });
32
+
33
+ console.log(result.upsertedKeys);
34
+ console.log(result.deletedKeys);
35
+ ```
36
+
37
+ The operation returns only `upsertedKeys` and `deletedKeys`. Submitted secret values are never returned or added to helper error messages.
38
+
39
+ ## Use An Explicit Client
40
+
41
+ Pass `client` when you need to target a particular Phoenix instance. Otherwise, the helper creates a client from the standard Phoenix environment configuration.
42
+
43
+ ```ts
44
+ import { createClient } from "@arizeai/phoenix-client";
45
+ import { upsertOrDeleteSecrets } from "@arizeai/phoenix-client/secrets";
46
+
47
+ const apiKey = process.env.OPENAI_API_KEY;
48
+ if (!apiKey) throw new Error("OPENAI_API_KEY is required");
49
+
50
+ const client = createClient({
51
+ options: { baseUrl: "https://phoenix.example.com" },
52
+ });
53
+
54
+ await upsertOrDeleteSecrets({
55
+ client,
56
+ secrets: [{ key: "OPENAI_API_KEY", value: apiKey }],
57
+ });
58
+ ```
59
+
60
+ <section className="hidden" data-agent-context="source-map" aria-label="Source map">
61
+ <h2>Source Map</h2>
62
+ <ul>
63
+ <li><code>src/secrets/index.ts</code></li>
64
+ <li><code>src/secrets/upsertOrDeleteSecrets.ts</code></li>
65
+ <li><code>src/client.ts</code></li>
66
+ <li><code>src/__generated__/api/v1.ts</code></li>
67
+ </ul>
68
+ </section>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arizeai/phoenix-client",
3
- "version": "7.12.0",
3
+ "version": "7.14.0",
4
4
  "description": "A client for the Phoenix API",
5
5
  "keywords": [
6
6
  "arize",
@@ -53,6 +53,10 @@
53
53
  "import": "./dist/esm/sessions/index.js",
54
54
  "require": "./dist/src/sessions/index.js"
55
55
  },
56
+ "./secrets": {
57
+ "import": "./dist/esm/secrets/index.js",
58
+ "require": "./dist/src/secrets/index.js"
59
+ },
56
60
  "./projects": {
57
61
  "import": "./dist/esm/projects/index.js",
58
62
  "require": "./dist/src/projects/index.js"
@@ -115,7 +119,7 @@
115
119
  "@ai-sdk/openai": "^4.0.71",
116
120
  "@ai-sdk/otel": "^1.0.107",
117
121
  "@anthropic-ai/sdk": "^0.111.0",
118
- "@arizeai/phoenix-evals": "2.5.0",
122
+ "@arizeai/phoenix-evals": "2.6.0",
119
123
  "@arizeai/phoenix-testing": "0.0.0",
120
124
  "@opentelemetry/api": "^1.9.1",
121
125
  "@opentelemetry/sdk-trace-node": "^2.11.0",
@@ -2679,7 +2679,7 @@ export interface components {
2679
2679
  };
2680
2680
  /**
2681
2681
  * Example Ids
2682
- * @description Optional dataset example IDs (GlobalIDs) to seed the split with. Each example must belong to this dataset. Omit to create an empty split.
2682
+ * @description Optional dataset example identifiers (GlobalIDs or user-provided IDs) to seed the split with. Each example must belong to this dataset. Omit to create an empty split.
2683
2683
  */
2684
2684
  example_ids?: string[];
2685
2685
  };
@@ -6716,12 +6716,12 @@ export interface components {
6716
6716
  } | null;
6717
6717
  /**
6718
6718
  * Add Example Ids
6719
- * @description Dataset example IDs (GlobalIDs) to add to the split. Each example must belong to this dataset. Adding an example already in the split is a no-op.
6719
+ * @description Dataset example identifiers (GlobalIDs or user-provided IDs) to add to the split. Each example must belong to this dataset. Adding an example already in the split is a no-op.
6720
6720
  */
6721
6721
  add_example_ids?: string[];
6722
6722
  /**
6723
6723
  * Remove Example Ids
6724
- * @description Dataset example IDs (GlobalIDs) to remove from the split.
6724
+ * @description Dataset example identifiers (GlobalIDs or user-provided IDs) to remove from the split.
6725
6725
  */
6726
6726
  remove_example_ids?: string[];
6727
6727
  };
@@ -147,6 +147,27 @@ export const DATASET_UPLOAD_EXAMPLE_IDS: ParameterRequirement = {
147
147
  minServerVersion: [15, 0, 0],
148
148
  };
149
149
 
150
+ export const CREATE_DATASET_SPLIT: RouteRequirement = {
151
+ kind: "route",
152
+ method: "POST",
153
+ path: "/v1/datasets/{dataset_identifier}/splits",
154
+ minServerVersion: [19, 20, 0],
155
+ };
156
+
157
+ export const UPDATE_DATASET_SPLIT: RouteRequirement = {
158
+ kind: "route",
159
+ method: "PATCH",
160
+ path: "/v1/datasets/{dataset_identifier}/splits/{split_id}",
161
+ minServerVersion: [19, 20, 0],
162
+ };
163
+
164
+ export const DELETE_DATASET_SPLIT: RouteRequirement = {
165
+ kind: "route",
166
+ method: "DELETE",
167
+ path: "/v1/datasets/{dataset_identifier}/splits/{split_id}",
168
+ minServerVersion: [19, 20, 0],
169
+ };
170
+
150
171
  export const ADD_TRACE_NOTE_IDENTIFIER: ParameterRequirement = {
151
172
  kind: "parameter",
152
173
  parameterName: "identifier",
@@ -265,6 +286,9 @@ export const ALL_REQUIREMENTS: readonly CapabilityRequirement[] = [
265
286
  LIST_SESSIONS_FILTER_EXPRESSION,
266
287
  TRANSFER_TRACES,
267
288
  DATASET_UPLOAD_EXAMPLE_IDS,
289
+ CREATE_DATASET_SPLIT,
290
+ UPDATE_DATASET_SPLIT,
291
+ DELETE_DATASET_SPLIT,
268
292
  ADD_TRACE_NOTE_IDENTIFIER,
269
293
  ADD_SPAN_NOTE_IDENTIFIER,
270
294
  ADD_SESSION_NOTE_IDENTIFIER,
@@ -0,0 +1,80 @@
1
+ import invariant from "tiny-invariant";
2
+
3
+ import type { components } from "../__generated__/api/v1";
4
+ import { createClient } from "../client";
5
+ import { CREATE_DATASET_SPLIT } from "../constants/serverRequirements";
6
+ import type { ClientFn } from "../types/core";
7
+ import type { DatasetIdentifier, DatasetSplit } from "../types/datasets";
8
+ import { ensureServerCapability } from "../utils/serverVersionUtils";
9
+ import { resolveDatasetIdentifier } from "./resolveDatasetIdentifier";
10
+
11
+ type CreateDatasetSplitRequestBody =
12
+ components["schemas"]["CreateDatasetSplitRequestBody"];
13
+ type CreateDatasetSplitResponseBody =
14
+ components["schemas"]["CreateDatasetSplitResponseBody"];
15
+
16
+ /** Parameters for creating a dataset split. */
17
+ export interface CreateDatasetSplitParams extends ClientFn {
18
+ /** The dataset, selected by name or GlobalID. */
19
+ dataset: DatasetIdentifier;
20
+ /** A unique name for the split. */
21
+ name: CreateDatasetSplitRequestBody["name"];
22
+ /** An optional description of the split. */
23
+ description?: CreateDatasetSplitRequestBody["description"];
24
+ /** An optional hex color for the split. */
25
+ color?: CreateDatasetSplitRequestBody["color"];
26
+ /** Arbitrary JSON metadata for the split. */
27
+ metadata?: CreateDatasetSplitRequestBody["metadata"];
28
+ /** Dataset example GlobalIDs or user-provided IDs with which to seed the split. */
29
+ exampleIds?: CreateDatasetSplitRequestBody["example_ids"];
30
+ }
31
+
32
+ /**
33
+ * Create a split on an existing dataset.
34
+ *
35
+ * @param params - The split to create.
36
+ * @param params.client - Optional Phoenix client instance.
37
+ * @param params.dataset - The dataset, selected by name or GlobalID.
38
+ * @param params.name - A unique name for the split.
39
+ * @param params.description - An optional description of the split.
40
+ * @param params.color - An optional hex color for the split.
41
+ * @param params.metadata - Arbitrary JSON metadata for the split.
42
+ * @param params.exampleIds - Dataset example GlobalIDs or user-provided IDs with which to seed the split.
43
+ * @returns The created dataset split.
44
+ * @throws {HttpError} If the dataset or an example does not exist, the name is
45
+ * already in use, or the request is invalid.
46
+ *
47
+ * @requires Phoenix server >= 19.20.0
48
+ */
49
+ export async function createDatasetSplit({
50
+ client: _client,
51
+ dataset,
52
+ name,
53
+ description,
54
+ color,
55
+ metadata,
56
+ exampleIds,
57
+ }: CreateDatasetSplitParams): Promise<DatasetSplit> {
58
+ const client = _client ?? createClient();
59
+ await ensureServerCapability({ client, requirement: CREATE_DATASET_SPLIT });
60
+ const datasetIdentifier = resolveDatasetIdentifier(dataset);
61
+
62
+ const body: CreateDatasetSplitRequestBody = {
63
+ name,
64
+ ...(description !== undefined ? { description } : {}),
65
+ ...(color !== undefined ? { color } : {}),
66
+ ...(metadata !== undefined ? { metadata } : {}),
67
+ ...(exampleIds !== undefined ? { example_ids: exampleIds } : {}),
68
+ };
69
+ const response = await client.POST(
70
+ "/v1/datasets/{dataset_identifier}/splits",
71
+ {
72
+ params: { path: { dataset_identifier: datasetIdentifier } },
73
+ body,
74
+ }
75
+ );
76
+ const responseBody: CreateDatasetSplitResponseBody | undefined =
77
+ response.data;
78
+ invariant(responseBody?.data, "Failed to create dataset split");
79
+ return responseBody.data;
80
+ }
@@ -0,0 +1,46 @@
1
+ import { createClient } from "../client";
2
+ import { DELETE_DATASET_SPLIT } from "../constants/serverRequirements";
3
+ import type { ClientFn } from "../types/core";
4
+ import type { DatasetIdentifier } from "../types/datasets";
5
+ import { ensureServerCapability } from "../utils/serverVersionUtils";
6
+ import { resolveDatasetIdentifier } from "./resolveDatasetIdentifier";
7
+
8
+ /** Parameters for deleting a dataset split. */
9
+ export interface DeleteDatasetSplitParams extends ClientFn {
10
+ /** The dataset, selected by name or GlobalID. */
11
+ dataset: DatasetIdentifier;
12
+ /** The dataset split GlobalID. */
13
+ splitId: string;
14
+ }
15
+
16
+ /**
17
+ * Delete a dataset split and its memberships without deleting its examples.
18
+ *
19
+ * @param params - The split to delete.
20
+ * @param params.client - Optional Phoenix client instance.
21
+ * @param params.dataset - The dataset, selected by name or GlobalID.
22
+ * @param params.splitId - The dataset split GlobalID.
23
+ * @returns A promise that resolves once the split is deleted.
24
+ * @throws {HttpError} If the dataset or split does not exist, or the split ID
25
+ * is invalid.
26
+ *
27
+ * @requires Phoenix server >= 19.20.0
28
+ */
29
+ export async function deleteDatasetSplit({
30
+ client: _client,
31
+ dataset,
32
+ splitId,
33
+ }: DeleteDatasetSplitParams): Promise<void> {
34
+ const client = _client ?? createClient();
35
+ await ensureServerCapability({ client, requirement: DELETE_DATASET_SPLIT });
36
+ const datasetIdentifier = resolveDatasetIdentifier(dataset);
37
+
38
+ await client.DELETE("/v1/datasets/{dataset_identifier}/splits/{split_id}", {
39
+ params: {
40
+ path: {
41
+ dataset_identifier: datasetIdentifier,
42
+ split_id: splitId,
43
+ },
44
+ },
45
+ });
46
+ }
@@ -1,5 +1,9 @@
1
1
  export * from "./createDataset";
2
+ export * from "./createDatasetSplit";
3
+ export * from "./deleteDatasetSplit";
2
4
  export * from "./getDataset";
3
5
  export * from "./getDatasetExamples";
4
6
  export * from "./appendDatasetExamples";
5
7
  export * from "./getDatasetInfo";
8
+ export * from "./updateDatasetSplit";
9
+ export type { DatasetIdentifier, DatasetSplit } from "../types/datasets";
@@ -0,0 +1,6 @@
1
+ import type { DatasetIdentifier } from "../types/datasets";
2
+
3
+ /** Resolve a typed dataset selector to the REST path identifier. */
4
+ export function resolveDatasetIdentifier(dataset: DatasetIdentifier): string {
5
+ return "datasetName" in dataset ? dataset.datasetName : dataset.datasetId;
6
+ }
@@ -0,0 +1,99 @@
1
+ import invariant from "tiny-invariant";
2
+
3
+ import type { components } from "../__generated__/api/v1";
4
+ import { createClient } from "../client";
5
+ import { UPDATE_DATASET_SPLIT } from "../constants/serverRequirements";
6
+ import type { ClientFn } from "../types/core";
7
+ import type { DatasetIdentifier, DatasetSplit } from "../types/datasets";
8
+ import { ensureServerCapability } from "../utils/serverVersionUtils";
9
+ import { resolveDatasetIdentifier } from "./resolveDatasetIdentifier";
10
+
11
+ type UpdateDatasetSplitRequestBody =
12
+ components["schemas"]["UpdateDatasetSplitRequestBody"];
13
+ type UpdateDatasetSplitResponseBody =
14
+ components["schemas"]["UpdateDatasetSplitResponseBody"];
15
+
16
+ /** Parameters for partially updating a dataset split. */
17
+ export interface UpdateDatasetSplitParams extends ClientFn {
18
+ /** The dataset, selected by name or GlobalID. */
19
+ dataset: DatasetIdentifier;
20
+ /** The dataset split GlobalID. */
21
+ splitId: string;
22
+ /** A new unique name for the split. */
23
+ name?: UpdateDatasetSplitRequestBody["name"];
24
+ /** A new description, or null to clear it. */
25
+ description?: UpdateDatasetSplitRequestBody["description"];
26
+ /** A new hex color for the split. */
27
+ color?: UpdateDatasetSplitRequestBody["color"];
28
+ /** JSON metadata that replaces the existing metadata. */
29
+ metadata?: UpdateDatasetSplitRequestBody["metadata"];
30
+ /** Dataset example GlobalIDs or user-provided IDs to add. Existing memberships are no-ops. */
31
+ addExampleIds?: UpdateDatasetSplitRequestBody["add_example_ids"];
32
+ /** Dataset example GlobalIDs or user-provided IDs to remove. Missing memberships are no-ops. */
33
+ removeExampleIds?: UpdateDatasetSplitRequestBody["remove_example_ids"];
34
+ }
35
+
36
+ /**
37
+ * Partially update a dataset split and/or its example membership.
38
+ *
39
+ * Only provided fields change. Membership additions and removals are
40
+ * idempotent; if an example appears in both arrays, removal wins.
41
+ *
42
+ * @param params - The split fields and memberships to update.
43
+ * @param params.client - Optional Phoenix client instance.
44
+ * @param params.dataset - The dataset, selected by name or GlobalID.
45
+ * @param params.splitId - The dataset split GlobalID.
46
+ * @param params.name - A new unique name for the split.
47
+ * @param params.description - A new description, or null to clear it.
48
+ * @param params.color - A new hex color for the split.
49
+ * @param params.metadata - JSON metadata that replaces the existing metadata.
50
+ * @param params.addExampleIds - Dataset example GlobalIDs or user-provided IDs to add.
51
+ * @param params.removeExampleIds - Dataset example GlobalIDs or user-provided IDs to remove.
52
+ * @returns The updated dataset split.
53
+ * @throws {HttpError} If the dataset, split, or an example does not exist, the
54
+ * name is already in use, or the request is invalid.
55
+ *
56
+ * @requires Phoenix server >= 19.20.0
57
+ */
58
+ export async function updateDatasetSplit({
59
+ client: _client,
60
+ dataset,
61
+ splitId,
62
+ name,
63
+ description,
64
+ color,
65
+ metadata,
66
+ addExampleIds,
67
+ removeExampleIds,
68
+ }: UpdateDatasetSplitParams): Promise<DatasetSplit> {
69
+ const client = _client ?? createClient();
70
+ await ensureServerCapability({ client, requirement: UPDATE_DATASET_SPLIT });
71
+ const datasetIdentifier = resolveDatasetIdentifier(dataset);
72
+
73
+ const body: UpdateDatasetSplitRequestBody = {
74
+ ...(name !== undefined ? { name } : {}),
75
+ ...(description !== undefined ? { description } : {}),
76
+ ...(color !== undefined ? { color } : {}),
77
+ ...(metadata !== undefined ? { metadata } : {}),
78
+ ...(addExampleIds !== undefined ? { add_example_ids: addExampleIds } : {}),
79
+ ...(removeExampleIds !== undefined
80
+ ? { remove_example_ids: removeExampleIds }
81
+ : {}),
82
+ };
83
+ const response = await client.PATCH(
84
+ "/v1/datasets/{dataset_identifier}/splits/{split_id}",
85
+ {
86
+ params: {
87
+ path: {
88
+ dataset_identifier: datasetIdentifier,
89
+ split_id: splitId,
90
+ },
91
+ },
92
+ body,
93
+ }
94
+ );
95
+ const responseBody: UpdateDatasetSplitResponseBody | undefined =
96
+ response.data;
97
+ invariant(responseBody?.data, "Failed to update dataset split");
98
+ return responseBody.data;
99
+ }
@@ -0,0 +1 @@
1
+ export * from "./upsertOrDeleteSecrets";
@@ -0,0 +1,87 @@
1
+ import { createClient } from "../client";
2
+ import type { ClientFn } from "../types/core";
3
+
4
+ /**
5
+ * A secret to create, update, or delete.
6
+ */
7
+ export interface SecretInput {
8
+ /** The environment-style key used to identify the secret. */
9
+ key: string;
10
+ /** A value to create or update, or `null` to delete the key. */
11
+ value: string | null;
12
+ }
13
+
14
+ /**
15
+ * Parameters for atomically updating secrets.
16
+ */
17
+ export interface UpsertOrDeleteSecretsParams extends ClientFn {
18
+ /**
19
+ * Ordered secret updates. When a key occurs more than once, the server
20
+ * applies only its last occurrence.
21
+ */
22
+ secrets: SecretInput[];
23
+ }
24
+
25
+ /**
26
+ * The names of the keys changed by a secrets update.
27
+ *
28
+ * Secret values are intentionally excluded.
29
+ */
30
+ export interface UpsertOrDeleteSecretsResult {
31
+ /** Keys that were created or updated. */
32
+ upsertedKeys: string[];
33
+ /** Keys that were deleted. */
34
+ deletedKeys: string[];
35
+ }
36
+
37
+ /**
38
+ * Atomically create, update, or delete a batch of Phoenix secrets.
39
+ *
40
+ * A non-null value creates or updates a secret, while `null` deletes it.
41
+ * Duplicate keys use the last occurrence in the batch. The result contains
42
+ * key names only; submitted values are never returned or logged.
43
+ *
44
+ * @param params - The secrets update.
45
+ * @param params.client - Optional Phoenix client instance.
46
+ * @param params.secrets - Ordered key/value-or-null updates.
47
+ * @returns The names of the keys that were upserted and deleted.
48
+ *
49
+ * @example
50
+ * ```ts
51
+ * import { upsertOrDeleteSecrets } from "@arizeai/phoenix-client/secrets";
52
+ *
53
+ * const apiKey = process.env.OPENAI_API_KEY;
54
+ * if (!apiKey) throw new Error("OPENAI_API_KEY is required");
55
+ *
56
+ * const result = await upsertOrDeleteSecrets({
57
+ * secrets: [
58
+ * { key: "OPENAI_API_KEY", value: apiKey },
59
+ * { key: "OLD_PROVIDER_API_KEY", value: null },
60
+ * ],
61
+ * });
62
+ * ```
63
+ */
64
+ export async function upsertOrDeleteSecrets({
65
+ client: _client,
66
+ secrets,
67
+ }: UpsertOrDeleteSecretsParams): Promise<UpsertOrDeleteSecretsResult> {
68
+ const client = _client ?? createClient();
69
+ const { data, error } = await client.PUT("/v1/secrets", {
70
+ body: { secrets },
71
+ });
72
+
73
+ // Do not include the server error payload here: validation responses can be
74
+ // influenced by submitted data, which must never enter helper error text.
75
+ if (error) {
76
+ throw new Error("Failed to upsert or delete secrets");
77
+ }
78
+
79
+ if (!data?.data) {
80
+ throw new Error("Failed to upsert or delete secrets: no data returned");
81
+ }
82
+
83
+ return {
84
+ upsertedKeys: data.data.upserted_keys,
85
+ deletedKeys: data.data.deleted_keys,
86
+ };
87
+ }
@@ -1,13 +1,18 @@
1
+ import type { components } from "../__generated__/api/v1";
1
2
  import type { Node } from "./core";
2
3
 
4
+ /** A named subset of examples in a dataset. */
5
+ export type DatasetSplit = components["schemas"]["DatasetSplit"];
6
+
3
7
  type DatasetSelectorBase = { versionId?: string; splits?: string[] };
4
8
 
9
+ /** A dataset identified by either its GlobalID or name. */
10
+ export type DatasetIdentifier = { datasetId: string } | { datasetName: string };
11
+
5
12
  /**
6
13
  * A dataset can be identified by its datasetId, datasetName, or datasetVersionId
7
14
  */
8
- export type DatasetSelector =
9
- | (DatasetSelectorBase & { datasetId: string })
10
- | (DatasetSelectorBase & { datasetName: string });
15
+ export type DatasetSelector = DatasetSelectorBase & DatasetIdentifier;
11
16
 
12
17
  /**
13
18
  * Overview information about a dataset