@arizeai/phoenix-client
Version:
A client for the Phoenix API
129 lines (119 loc) • 4.22 kB
text/typescript
import invariant from "tiny-invariant";
import { createClient } from "../client";
import { DATASET_UPLOAD_EXAMPLE_IDS } from "../constants/serverRequirements";
import type { ClientFn } from "../types/core";
import type { DatasetSelector, Example } from "../types/datasets";
import { ensureServerCapability } from "../utils/serverVersionUtils";
import { getDatasetInfo } from "./getDatasetInfo";
export type AppendDatasetExamplesParams = ClientFn & {
/**
* The dataset to append examples to
*/
dataset: DatasetSelector;
/**
* The examples to append to the dataset
*/
examples: Example[];
};
export type AppendDatasetExamplesResponse = {
datasetId: string;
versionId: string;
};
/**
* Append examples to an existing dataset.
*
* @experimental this interface may change in the future
*
* @param params - The parameters for appending examples
* @param params.client - Optional Phoenix client instance
* @param params.dataset - The dataset to append examples to (by ID or name)
* @param params.examples - The examples to append. Each example can include:
* - `input`: Required input data for the example
* - `output`: Optional expected output data
* - `metadata`: Optional metadata for the example
* - `splits`: Optional split assignment (string, array of strings, or null)
* - `spanId`: Optional OpenTelemetry span ID to link the example back to its source span
* - `id`: Optional stable ID for the example, used to reference or update it later
*
* @returns A promise that resolves to the dataset ID and version ID
*
* @example
* ```ts
* const { datasetId, versionId } = await appendDatasetExamples({
* dataset: { datasetName: "qa-dataset" },
* examples: [
* {
* input: { question: "What is deep learning?" },
* output: { answer: "Deep learning is..." },
* spanId: "span123abc" // Links to the source span
* },
* {
* id: "my-stable-id", // Stable ID for referencing this example later
* input: { question: "What is a transformer?" },
* output: { answer: "A transformer is..." },
* }
* ]
* });
* ```
*/
export async function appendDatasetExamples({
client: _client,
dataset,
examples,
}: AppendDatasetExamplesParams): Promise<AppendDatasetExamplesResponse> {
const client = _client || createClient();
const inputs = examples.map((example) => example.input);
const outputs = examples.map((example) => example.output ?? {}); // Treat null as an empty object
const metadata = examples.map((example) => example.metadata ?? {});
const splits = examples.map((example) =>
example.splits !== undefined ? example.splits : null
);
// Extract span IDs from examples, preserving null/undefined as null
const spanIds = examples.map((example) => example?.spanId ?? null);
// Only include span_ids in the request if at least one example has a span ID
const hasSpanIds = spanIds.some((id) => id !== null);
// Extract example IDs from examples, preserving null/undefined as null
const exampleIds = examples.map((example) => example?.id ?? null);
// Only include example_ids in the request if at least one example has an ID
const hasExampleIds = exampleIds.some((id) => id !== null);
let datasetName: string;
if ("datasetName" in dataset) {
datasetName = dataset.datasetName;
} else {
const datasetInfo = await getDatasetInfo({
client,
dataset,
});
datasetName = datasetInfo.name;
}
if (hasExampleIds) {
await ensureServerCapability({
client,
requirement: DATASET_UPLOAD_EXAMPLE_IDS,
});
}
const appendResponse = await client.POST("/v1/datasets/upload", {
params: {
query: {
sync: true,
},
},
body: {
name: datasetName,
action: "append",
inputs,
outputs,
metadata,
splits,
...(hasSpanIds ? { span_ids: spanIds } : {}),
...(hasExampleIds ? { example_ids: exampleIds } : {}),
},
});
invariant(appendResponse.data?.data, "Failed to append dataset examples");
const datasetId = appendResponse.data.data.dataset_id;
const versionId = appendResponse.data.data.version_id;
return {
datasetId,
versionId,
};
}