diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
deleted file mode 100644
index 8ea34be..0000000
--- a/.devcontainer/Dockerfile
+++ /dev/null
@@ -1,23 +0,0 @@
-# syntax=docker/dockerfile:1
-FROM debian:bookworm-slim AS stainless
-
-RUN apt-get update && apt-get install -y \
- nodejs \
- npm \
- yarnpkg \
- && apt-get clean autoclean
-
-# Ensure UTF-8 encoding
-ENV LANG=C.UTF-8
-ENV LC_ALL=C.UTF-8
-
-# Yarn
-RUN ln -sf /usr/bin/yarnpkg /usr/bin/yarn
-
-WORKDIR /workspace
-
-COPY package.json yarn.lock /workspace/
-
-RUN yarn install
-
-COPY . /workspace
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index d55fc4d..763462f 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -1,20 +1,17 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/debian
{
- "name": "Debian",
- "build": {
- "dockerfile": "Dockerfile"
+ "name": "Development",
+ "image": "mcr.microsoft.com/devcontainers/typescript-node:latest",
+ "features": {
+ "ghcr.io/devcontainers/features/node:1": {}
+ },
+ "postCreateCommand": "yarn install",
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "esbenp.prettier-vscode"
+ ]
+ }
}
-
- // Features to add to the dev container. More info: https://containers.dev/features.
- // "features": {},
-
- // Use 'forwardPorts' to make a list of ports inside the container available locally.
- // "forwardPorts": [],
-
- // Configure tool-specific properties.
- // "customizations": {},
-
- // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
- // "remoteUser": "root"
}
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index ddb531a..c7ba1ce 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,19 +1,23 @@
name: CI
on:
push:
- branches:
- - main
+ branches-ignore:
+ - 'generated'
+ - 'codegen/**'
+ - 'integrated/**'
+ - 'stl-preview-head/**'
+ - 'stl-preview-base/**'
pull_request:
- branches:
- - main
- - next
+ branches-ignore:
+ - 'stl-preview-head/**'
+ - 'stl-preview-base/**'
jobs:
lint:
+ timeout-minutes: 10
name: lint
- runs-on: ubuntu-latest
-
-
+ runs-on: ${{ github.repository == 'stainless-sdks/openlayer-node' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
+ if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
steps:
- uses: actions/checkout@v4
@@ -29,10 +33,13 @@ jobs:
run: ./scripts/lint
build:
+ timeout-minutes: 5
name: build
- runs-on: ubuntu-latest
-
-
+ runs-on: ${{ github.repository == 'stainless-sdks/openlayer-node' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
+ if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
+ permissions:
+ contents: read
+ id-token: write
steps:
- uses: actions/checkout@v4
@@ -46,3 +53,18 @@ jobs:
- name: Check build
run: ./scripts/build
+
+ - name: Get GitHub OIDC Token
+ if: github.repository == 'stainless-sdks/openlayer-node'
+ id: github-oidc
+ uses: actions/github-script@v6
+ with:
+ script: core.setOutput('github_token', await core.getIDToken());
+
+ - name: Upload tarball
+ if: github.repository == 'stainless-sdks/openlayer-node'
+ env:
+ URL: https://pkg.stainless.com/s
+ AUTH: ${{ steps.github-oidc.outputs.github_token }}
+ SHA: ${{ github.sha }}
+ run: ./scripts/utils/upload-artifact.sh
diff --git a/.github/workflows/publish-npm.yml b/.github/workflows/publish-npm.yml
index 361a64a..85aedac 100644
--- a/.github/workflows/publish-npm.yml
+++ b/.github/workflows/publish-npm.yml
@@ -19,7 +19,7 @@ jobs:
- name: Set up Node
uses: actions/setup-node@v3
with:
- node-version: '18'
+ node-version: '20'
- name: Install dependencies
run: |
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 8032c17..ed21d28 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.12.0"
+ ".": "0.13.0"
}
diff --git a/.stats.yml b/.stats.yml
index c254947..2b09528 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1 +1,3 @@
-configured_endpoints: 15
+configured_endpoints: 18
+openapi_spec_hash: 20f058101a252f7500803d66aff58eb3
+config_hash: 30422a4611d93ca69e4f1aff60b9ddb5
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2b97fbb..8d15e01 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,79 @@
# Changelog
+## 0.13.0 (2025-07-03)
+
+Full Changelog: [v0.12.0...v0.13.0](https://github.com/openlayer-ai/openlayer-ts/compare/v0.12.0...v0.13.0)
+
+### Features
+
+* **api:** add test creation endpoint ([b7cc1b1](https://github.com/openlayer-ai/openlayer-ts/commit/b7cc1b1ee12af1275addac7057d735fb02a43af1))
+* **api:** api update ([372b228](https://github.com/openlayer-ai/openlayer-ts/commit/372b228b95947b7ccb8d6acc9a35eb7a20a31ef6))
+* **api:** api update ([8e2a339](https://github.com/openlayer-ai/openlayer-ts/commit/8e2a33931654d6802087b6435a4beb2c6ed6d7e1))
+* **api:** api update ([2e0a6e1](https://github.com/openlayer-ai/openlayer-ts/commit/2e0a6e1b6a5b3296a3ec905dfa11b5a8501e4583))
+* **api:** api update ([821a692](https://github.com/openlayer-ai/openlayer-ts/commit/821a6926192dad0532029ea9c2b22a4d24f5e1c6))
+* **api:** api update ([28f4085](https://github.com/openlayer-ai/openlayer-ts/commit/28f40851950cfa354930fc8dfab69b4a39f3d13e))
+* **api:** api update ([3345b62](https://github.com/openlayer-ai/openlayer-ts/commit/3345b629c1904bfb2c2f9e05c86312c6c7b88b48))
+* **api:** expose test retrieval endpoint ([da2b9fb](https://github.com/openlayer-ai/openlayer-ts/commit/da2b9fb62bd0eeaebe1325abaee6e4854d2f987f))
+* **api:** expose test retrieval endpoint ([f3fd3fc](https://github.com/openlayer-ai/openlayer-ts/commit/f3fd3fc296af8982eeb965c3427b89abdb7a4a6f))
+* **api:** expose test update endpoint ([785d3b4](https://github.com/openlayer-ai/openlayer-ts/commit/785d3b4ba67f487b49c190d546ae4a7dc7eb333b))
+* **client:** add support for endpoint-specific base URLs ([6738a63](https://github.com/openlayer-ai/openlayer-ts/commit/6738a6324eb5b4bb15db942a8b6de7cf267df1ac))
+* **client:** send `X-Stainless-Timeout` header ([333edef](https://github.com/openlayer-ai/openlayer-ts/commit/333edef002f95acecdb29d3e6a698343c3361937))
+
+
+### Bug Fixes
+
+* **api:** improve type resolution when importing as a package ([#119](https://github.com/openlayer-ai/openlayer-ts/issues/119)) ([669c259](https://github.com/openlayer-ai/openlayer-ts/commit/669c259b9fa3ba40ace9901ba24fa80dbc205105))
+* avoid type error in certain environments ([#115](https://github.com/openlayer-ai/openlayer-ts/issues/115)) ([701091f](https://github.com/openlayer-ai/openlayer-ts/commit/701091ff829ca7b7f6ee3bf66af961b660eb3371))
+* **ci:** release-doctor — report correct token name ([d176ef5](https://github.com/openlayer-ai/openlayer-ts/commit/d176ef5f74d7955bbc2a58b4d3f70739e0050962))
+* **client:** don't send `Content-Type` for bodyless methods ([d96149f](https://github.com/openlayer-ai/openlayer-ts/commit/d96149ff67a4d0cce9ba465c6888a10335df235d))
+* **client:** fix export map for index exports ([ac3dffc](https://github.com/openlayer-ai/openlayer-ts/commit/ac3dffce4624d350d5cf73e3020067cb2f71ab59))
+* **client:** send `X-Stainless-Timeout` in seconds ([#117](https://github.com/openlayer-ai/openlayer-ts/issues/117)) ([b8469f3](https://github.com/openlayer-ai/openlayer-ts/commit/b8469f3c2fc46916d994fb20b6ff4550a1c2bed3))
+* **internal:** work around https://github.com/vercel/next.js/issues/76881 ([#116](https://github.com/openlayer-ai/openlayer-ts/issues/116)) ([2349019](https://github.com/openlayer-ai/openlayer-ts/commit/2349019081867529df94ff1143716ce865a30007))
+* **mcp:** remove unused tools.ts ([#120](https://github.com/openlayer-ai/openlayer-ts/issues/120)) ([9d9ab9b](https://github.com/openlayer-ai/openlayer-ts/commit/9d9ab9bd791a42fc4269ad0fcd1dbb57f8616e67))
+* publish script — handle NPM errors correctly ([2cbaaa0](https://github.com/openlayer-ai/openlayer-ts/commit/2cbaaa0a255b0a39ddb4a1d7908a7998a89c8247))
+
+
+### Chores
+
+* **ci:** add timeout thresholds for CI jobs ([64632ad](https://github.com/openlayer-ai/openlayer-ts/commit/64632ad55c30e27a0ca063c28b410d6b83b245b4))
+* **ci:** bump node version for release workflows ([fc5eda1](https://github.com/openlayer-ai/openlayer-ts/commit/fc5eda10a988522c2304c3f7a8f1890db0f42a23))
+* **ci:** enable for pull requests ([4f54573](https://github.com/openlayer-ai/openlayer-ts/commit/4f545736a41460e289815a842018a5dc1993e47e))
+* **ci:** only run for pushes and fork pull requests ([057c965](https://github.com/openlayer-ai/openlayer-ts/commit/057c96531b52e2fa19ece0c6affdae4d5982f79d))
+* **ci:** only use depot for staging repos ([9c8f4b9](https://github.com/openlayer-ai/openlayer-ts/commit/9c8f4b9181f636898c5ff8365596b47c591575f1))
+* **client:** minor internal fixes ([344120f](https://github.com/openlayer-ai/openlayer-ts/commit/344120f8eb82ea1e374aafadf6c7e90edec2cbce))
+* **docs:** grammar improvements ([1f62b55](https://github.com/openlayer-ai/openlayer-ts/commit/1f62b551d8afd5b4fa17595b13df6765c8cc5c94))
+* **docs:** use top-level-await in example snippets ([a0bfc1a](https://github.com/openlayer-ai/openlayer-ts/commit/a0bfc1ac34aa7b3f929470cbcfe00fbdd2428566))
+* **exports:** cleaner resource index imports ([#113](https://github.com/openlayer-ai/openlayer-ts/issues/113)) ([9ac3180](https://github.com/openlayer-ai/openlayer-ts/commit/9ac318013fd2128e7f0a98db699534569c8e18a3))
+* **exports:** stop using path fallbacks ([#114](https://github.com/openlayer-ai/openlayer-ts/issues/114)) ([fdcd6e2](https://github.com/openlayer-ai/openlayer-ts/commit/fdcd6e2effcd89201604c545e0c3b8f0f0800032))
+* improve publish-npm script --latest tag logic ([baabb5f](https://github.com/openlayer-ai/openlayer-ts/commit/baabb5fbba3cf1f689dbadafc6cc027030603460))
+* **internal:** add aliases for Record and Array ([#118](https://github.com/openlayer-ai/openlayer-ts/issues/118)) ([da4702c](https://github.com/openlayer-ai/openlayer-ts/commit/da4702cab36298cc7278a3f0a797742c5e23a002))
+* **internal:** codegen related update ([08863b8](https://github.com/openlayer-ai/openlayer-ts/commit/08863b80cb9f578fada7d596b0013f76f9d80f4e))
+* **internal:** codegen related update ([e1c6120](https://github.com/openlayer-ai/openlayer-ts/commit/e1c612061c9dceffc20cb78c03bcdea4e466c559))
+* **internal:** codegen related update ([1128c8f](https://github.com/openlayer-ai/openlayer-ts/commit/1128c8f19942ebfecc014626f859dfdadc5725d1))
+* **internal:** codegen related update ([1016df6](https://github.com/openlayer-ai/openlayer-ts/commit/1016df6f549fd3312c26f6ecfd15b3aac58c5d65))
+* **internal:** codegen related update ([ca17307](https://github.com/openlayer-ai/openlayer-ts/commit/ca173073bc522424d9d6a35b5a2af14ccd7b8c88))
+* **internal:** codegen related update ([fd5d0a9](https://github.com/openlayer-ai/openlayer-ts/commit/fd5d0a9808b2abf8d1b88d3becf2350d9b25887e))
+* **internal:** fix devcontainers setup ([6a459de](https://github.com/openlayer-ai/openlayer-ts/commit/6a459de24ebec36ba38b751b6ab7143b3b9e16fd))
+* **internal:** fix workflows ([59a7cb4](https://github.com/openlayer-ai/openlayer-ts/commit/59a7cb4439a344c0ed6b4adb9e6b113dbed0f940))
+* **internal:** make base APIResource abstract ([53aa0dc](https://github.com/openlayer-ai/openlayer-ts/commit/53aa0dc14e5163e2b69430cd3b46ee2f5f8cb725))
+* **internal:** reduce CI branch coverage ([71998ef](https://github.com/openlayer-ai/openlayer-ts/commit/71998ef9132f29569e40aa1310cf2b3611096865))
+* **internal:** upload builds and expand CI branch coverage ([5d8ec66](https://github.com/openlayer-ai/openlayer-ts/commit/5d8ec66ad5ca7ba34da7b56b5f2aa1e9638107cd))
+* **internal:** version bump ([3a31790](https://github.com/openlayer-ai/openlayer-ts/commit/3a317902d8bf523f3f54398cdb077a11a18d9995))
+* mention unit type in timeout docs ([44c94cb](https://github.com/openlayer-ai/openlayer-ts/commit/44c94cb55dc3d3df305a31e3c28d66b37730eb9d))
+* **tests:** improve enum examples ([#121](https://github.com/openlayer-ai/openlayer-ts/issues/121)) ([f3a00ab](https://github.com/openlayer-ai/openlayer-ts/commit/f3a00ab15a42239fc71459feb324bd835f5d79e9))
+
+
+### Documentation
+
+* add examples to tsdocs ([8b8c71e](https://github.com/openlayer-ai/openlayer-ts/commit/8b8c71e6186135f9ecef0fcb00518946b05ddf71))
+* **readme:** fix typo ([3461fd6](https://github.com/openlayer-ai/openlayer-ts/commit/3461fd6e7a989064ef94b9e1d0c8d26374116aa4))
+* update URLs from stainlessapi.com to stainless.com ([e56ef04](https://github.com/openlayer-ai/openlayer-ts/commit/e56ef04346aa002b8481a8018ad540c678a9a21e))
+
+
+### Refactors
+
+* **types:** replace Record with mapped types ([678eaa3](https://github.com/openlayer-ai/openlayer-ts/commit/678eaa32437204655c81d34894038e4037026f05))
+
## 0.12.0 (2025-03-14)
Full Changelog: [v0.11.0...v0.12.0](https://github.com/openlayer-ai/openlayer-ts/compare/v0.11.0...v0.12.0)
diff --git a/README.md b/README.md
index c0a8352..acd4315 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@ This library provides convenient access to the Openlayer REST API from server-si
The REST API documentation can be found on [openlayer.com](https://openlayer.com/docs/api-reference/rest/overview). The full API of this library can be found in [api.md](api.md).
-It is generated with [Stainless](https://www.stainlessapi.com/).
+It is generated with [Stainless](https://www.stainless.com/).
## Installation
@@ -26,30 +26,26 @@ const client = new Openlayer({
apiKey: process.env['OPENLAYER_API_KEY'], // This is the default and can be omitted
});
-async function main() {
- const response = await client.inferencePipelines.data.stream('182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e', {
- config: {
- inputVariableNames: ['user_query'],
- outputColumnName: 'output',
- numOfTokenColumnName: 'tokens',
- costColumnName: 'cost',
- timestampColumnName: 'timestamp',
+const response = await client.inferencePipelines.data.stream('182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e', {
+ config: {
+ inputVariableNames: ['user_query'],
+ outputColumnName: 'output',
+ numOfTokenColumnName: 'tokens',
+ costColumnName: 'cost',
+ timestampColumnName: 'timestamp',
+ },
+ rows: [
+ {
+ user_query: 'what is the meaning of life?',
+ output: '42',
+ tokens: 7,
+ cost: 0.02,
+ timestamp: 1610000000,
},
- rows: [
- {
- user_query: 'what is the meaning of life?',
- output: '42',
- tokens: 7,
- cost: 0.02,
- timestamp: 1610000000,
- },
- ],
- });
-
- console.log(response.success);
-}
+ ],
+});
-main();
+console.log(response.success);
```
### Request & Response types
@@ -64,8 +60,42 @@ const client = new Openlayer({
apiKey: process.env['OPENLAYER_API_KEY'], // This is the default and can be omitted
});
-async function main() {
- const params: Openlayer.InferencePipelines.DataStreamParams = {
+const params: Openlayer.InferencePipelines.DataStreamParams = {
+ config: {
+ inputVariableNames: ['user_query'],
+ outputColumnName: 'output',
+ numOfTokenColumnName: 'tokens',
+ costColumnName: 'cost',
+ timestampColumnName: 'timestamp',
+ },
+ rows: [
+ {
+ user_query: 'what is the meaning of life?',
+ output: '42',
+ tokens: 7,
+ cost: 0.02,
+ timestamp: 1610000000,
+ },
+ ],
+};
+const response: Openlayer.InferencePipelines.DataStreamResponse = await client.inferencePipelines.data.stream(
+ '182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e',
+ params,
+);
+```
+
+Documentation for each method, request param, and response field are available in docstrings and will appear on hover in most modern editors.
+
+## Handling errors
+
+When the library is unable to connect to the API,
+or if the API returns a non-success status code (i.e., 4xx or 5xx response),
+a subclass of `APIError` will be thrown:
+
+
+```ts
+const response = await client.inferencePipelines.data
+ .stream('182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e', {
config: {
inputVariableNames: ['user_query'],
outputColumnName: 'output',
@@ -82,59 +112,19 @@ async function main() {
timestamp: 1610000000,
},
],
- };
- const response: Openlayer.InferencePipelines.DataStreamResponse =
- await client.inferencePipelines.data.stream('182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e', params);
-}
-
-main();
-```
-
-Documentation for each method, request param, and response field are available in docstrings and will appear on hover in most modern editors.
-
-## Handling errors
-
-When the library is unable to connect to the API,
-or if the API returns a non-success status code (i.e., 4xx or 5xx response),
-a subclass of `APIError` will be thrown:
-
-
-```ts
-async function main() {
- const response = await client.inferencePipelines.data
- .stream('182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e', {
- config: {
- inputVariableNames: ['user_query'],
- outputColumnName: 'output',
- numOfTokenColumnName: 'tokens',
- costColumnName: 'cost',
- timestampColumnName: 'timestamp',
- },
- rows: [
- {
- user_query: 'what is the meaning of life?',
- output: '42',
- tokens: 7,
- cost: 0.02,
- timestamp: 1610000000,
- },
- ],
- })
- .catch(async (err) => {
- if (err instanceof Openlayer.APIError) {
- console.log(err.status); // 400
- console.log(err.name); // BadRequestError
- console.log(err.headers); // {server: 'nginx', ...}
- } else {
- throw err;
- }
- });
-}
-
-main();
+ })
+ .catch(async (err) => {
+ if (err instanceof Openlayer.APIError) {
+ console.log(err.status); // 400
+ console.log(err.name); // BadRequestError
+ console.log(err.headers); // {server: 'nginx', ...}
+ } else {
+ throw err;
+ }
+ });
```
-Error codes are as followed:
+Error codes are as follows:
| Status Code | Error Type |
| ----------- | -------------------------- |
@@ -380,7 +370,7 @@ await client.inferencePipelines.data.stream(
This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions:
1. Changes that only affect static types, without breaking runtime behavior.
-2. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals)_.
+2. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals.)_
3. Changes that we do not expect to impact the vast majority of users in practice.
We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience.
diff --git a/SECURITY.md b/SECURITY.md
index 6dfa13e..dc108d0 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -2,9 +2,9 @@
## Reporting Security Issues
-This SDK is generated by [Stainless Software Inc](http://stainlessapi.com). Stainless takes security seriously, and encourages you to report any security vulnerability promptly so that appropriate action can be taken.
+This SDK is generated by [Stainless Software Inc](http://stainless.com). Stainless takes security seriously, and encourages you to report any security vulnerability promptly so that appropriate action can be taken.
-To report a security issue, please contact the Stainless team at security@stainlessapi.com.
+To report a security issue, please contact the Stainless team at security@stainless.com.
## Responsible Disclosure
@@ -16,11 +16,11 @@ before making any information public.
## Reporting Non-SDK Related Security Issues
If you encounter security issues that are not directly related to SDKs but pertain to the services
-or products provided by Openlayer please follow the respective company's security reporting guidelines.
+or products provided by Openlayer, please follow the respective company's security reporting guidelines.
### Openlayer Terms and Policies
-Please contact support@openlayer.com for any questions or concerns regarding security of our services.
+Please contact support@openlayer.com for any questions or concerns regarding the security of our services.
---
diff --git a/api.md b/api.md
index 93b4252..6de15d2 100644
--- a/api.md
+++ b/api.md
@@ -34,6 +34,20 @@ Methods:
- client.projects.inferencePipelines.create(projectId, { ...params }) -> InferencePipelineCreateResponse
- client.projects.inferencePipelines.list(projectId, { ...params }) -> InferencePipelineListResponse
+## Tests
+
+Types:
+
+- TestCreateResponse
+- TestUpdateResponse
+- TestListResponse
+
+Methods:
+
+- client.projects.tests.create(projectId, { ...params }) -> TestCreateResponse
+- client.projects.tests.update(projectId, { ...params }) -> TestUpdateResponse
+- client.projects.tests.list(projectId, { ...params }) -> TestListResponse
+
# Commits
Types:
diff --git a/bin/check-release-environment b/bin/check-release-environment
index 0f4fafe..e4b6d58 100644
--- a/bin/check-release-environment
+++ b/bin/check-release-environment
@@ -3,7 +3,7 @@
errors=()
if [ -z "${NPM_TOKEN}" ]; then
- errors+=("The OPENLAYER_NPM_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets")
+ errors+=("The NPM_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets")
fi
lenErrors=${#errors[@]}
diff --git a/bin/publish-npm b/bin/publish-npm
index 4c21181..fa2243d 100644
--- a/bin/publish-npm
+++ b/bin/publish-npm
@@ -4,19 +4,55 @@ set -eux
npm config set '//registry.npmjs.org/:_authToken' "$NPM_TOKEN"
-# Build the project
yarn build
-
-# Navigate to the dist directory
cd dist
-# Get the version from package.json
-VERSION="$(node -p "require('./package.json').version")"
+# Get package name and version from package.json
+PACKAGE_NAME="$(jq -r -e '.name' ./package.json)"
+VERSION="$(jq -r -e '.version' ./package.json)"
+
+# Get latest version from npm
+#
+# If the package doesn't exist, npm will return:
+# {
+# "error": {
+# "code": "E404",
+# "summary": "Unpublished on 2025-06-05T09:54:53.528Z",
+# "detail": "'the_package' is not in this registry..."
+# }
+# }
+NPM_INFO="$(npm view "$PACKAGE_NAME" version --json 2>/dev/null || true)"
+
+# Check if we got an E404 error
+if echo "$NPM_INFO" | jq -e '.error.code == "E404"' > /dev/null 2>&1; then
+ # Package doesn't exist yet, no last version
+ LAST_VERSION=""
+elif echo "$NPM_INFO" | jq -e '.error' > /dev/null 2>&1; then
+ # Report other errors
+ echo "ERROR: npm returned unexpected data:"
+ echo "$NPM_INFO"
+ exit 1
+else
+ # Success - get the version
+ LAST_VERSION=$(echo "$NPM_INFO" | jq -r '.') # strip quotes
+fi
-# Extract the pre-release tag if it exists
+# Check if current version is pre-release (e.g. alpha / beta / rc)
+CURRENT_IS_PRERELEASE=false
if [[ "$VERSION" =~ -([a-zA-Z]+) ]]; then
- # Extract the part before any dot in the pre-release identifier
- TAG="${BASH_REMATCH[1]}"
+ CURRENT_IS_PRERELEASE=true
+ CURRENT_TAG="${BASH_REMATCH[1]}"
+fi
+
+# Check if last version is a stable release
+LAST_IS_STABLE_RELEASE=true
+if [[ -z "$LAST_VERSION" || "$LAST_VERSION" =~ -([a-zA-Z]+) ]]; then
+ LAST_IS_STABLE_RELEASE=false
+fi
+
+# Use a corresponding alpha/beta tag if there already is a stable release and we're publishing a prerelease.
+if $CURRENT_IS_PRERELEASE && $LAST_IS_STABLE_RELEASE; then
+ TAG="$CURRENT_TAG"
else
TAG="latest"
fi
diff --git a/package.json b/package.json
index 13eefa3..b6df9a9 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "openlayer",
- "version": "0.12.0",
+ "version": "0.13.0",
"description": "The official TypeScript library for the Openlayer API",
"author": "Openlayer ",
"types": "dist/index.d.ts",
diff --git a/scripts/build b/scripts/build
index f174ead..46c2547 100755
--- a/scripts/build
+++ b/scripts/build
@@ -28,7 +28,7 @@ fi
node scripts/utils/make-dist-package-json.cjs > dist/package.json
# build to .js/.mjs/.d.ts files
-npm exec tsc-multi
+./node_modules/.bin/tsc-multi
# copy over handwritten .js/.mjs/.d.ts files
cp src/_shims/*.{d.ts,js,mjs,md} dist/_shims
cp src/_shims/auto/*.{d.ts,js,mjs} dist/_shims/auto
diff --git a/scripts/utils/upload-artifact.sh b/scripts/utils/upload-artifact.sh
new file mode 100755
index 0000000..380c00e
--- /dev/null
+++ b/scripts/utils/upload-artifact.sh
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+set -exuo pipefail
+
+RESPONSE=$(curl -X POST "$URL" \
+ -H "Authorization: Bearer $AUTH" \
+ -H "Content-Type: application/json")
+
+SIGNED_URL=$(echo "$RESPONSE" | jq -r '.url')
+
+if [[ "$SIGNED_URL" == "null" ]]; then
+ echo -e "\033[31mFailed to get signed URL.\033[0m"
+ exit 1
+fi
+
+UPLOAD_RESPONSE=$(tar -cz dist | curl -v -X PUT \
+ -H "Content-Type: application/gzip" \
+ --data-binary @- "$SIGNED_URL" 2>&1)
+
+if echo "$UPLOAD_RESPONSE" | grep -q "HTTP/[0-9.]* 200"; then
+ echo -e "\033[32mUploaded build to Stainless storage.\033[0m"
+ echo -e "\033[32mInstallation: npm install 'https://pkg.stainless.com/s/openlayer-node/$SHA'\033[0m"
+else
+ echo -e "\033[31mFailed to upload artifact.\033[0m"
+ exit 1
+fi
diff --git a/src/_shims/index-deno.ts b/src/_shims/index-deno.ts
index f98dad2..e755878 100644
--- a/src/_shims/index-deno.ts
+++ b/src/_shims/index-deno.ts
@@ -108,3 +108,5 @@ export declare class FsReadStream extends Readable {
const _ReadableStream = ReadableStream;
type _ReadableStream = ReadableStream;
export { _ReadableStream as ReadableStream };
+
+export const init = () => {};
diff --git a/src/_shims/index.d.ts b/src/_shims/index.d.ts
index 63ce99a..e3d9fc4 100644
--- a/src/_shims/index.d.ts
+++ b/src/_shims/index.d.ts
@@ -79,3 +79,5 @@ export function fileFromPath(path: string, options?: FileFromPathOptions): Promi
export function fileFromPath(path: string, filename?: string, options?: FileFromPathOptions): Promise;
export function isFsReadStream(value: any): value is FsReadStream;
+
+export const init: () => void;
diff --git a/src/_shims/index.js b/src/_shims/index.js
index 6b777dd..639ea48 100644
--- a/src/_shims/index.js
+++ b/src/_shims/index.js
@@ -3,7 +3,9 @@
*/
const shims = require('./registry');
const auto = require('openlayer/_shims/auto/runtime');
-if (!shims.kind) shims.setShims(auto.getRuntime(), { auto: true });
+exports.init = () => {
+ if (!shims.kind) shims.setShims(auto.getRuntime(), { auto: true });
+};
for (const property of Object.keys(shims)) {
Object.defineProperty(exports, property, {
get() {
@@ -11,3 +13,5 @@ for (const property of Object.keys(shims)) {
},
});
}
+
+exports.init();
diff --git a/src/_shims/index.mjs b/src/_shims/index.mjs
index 624fd3d..159fb36 100644
--- a/src/_shims/index.mjs
+++ b/src/_shims/index.mjs
@@ -3,5 +3,9 @@
*/
import * as shims from './registry.mjs';
import * as auto from 'openlayer/_shims/auto/runtime';
-if (!shims.kind) shims.setShims(auto.getRuntime(), { auto: true });
+export const init = () => {
+ if (!shims.kind) shims.setShims(auto.getRuntime(), { auto: true });
+};
export * from './registry.mjs';
+
+init();
diff --git a/src/core.ts b/src/core.ts
index 5981ff8..10e3c93 100644
--- a/src/core.ts
+++ b/src/core.ts
@@ -16,7 +16,12 @@ import {
type RequestInit,
type Response,
type HeadersInit,
+ init,
} from './_shims/index';
+
+// try running side effects outside of _shims/index to workaround https://github.com/vercel/next.js/issues/76881
+init();
+
export { type Response };
import { BlobLike, isBlobLike, isMultipartBody } from './uploads';
export {
@@ -28,6 +33,20 @@ export {
export type Fetch = (url: RequestInfo, init?: RequestInit) => Promise;
+/**
+ * An alias to the builtin `Array` type so we can
+ * easily alias it in import statements if there are name clashes.
+ */
+type _Array = Array;
+
+/**
+ * An alias to the builtin `Record` type so we can
+ * easily alias it in import statements if there are name clashes.
+ */
+type _Record = Record;
+
+export type { _Array as Array, _Record as Record };
+
type PromiseOrValue = T | Promise;
type APIResponseProps = {
@@ -151,6 +170,7 @@ export class APIPromise extends Promise {
export abstract class APIClient {
baseURL: string;
+ #baseURLOverridden: boolean;
maxRetries: number;
timeout: number;
httpAgent: Agent | undefined;
@@ -160,18 +180,21 @@ export abstract class APIClient {
constructor({
baseURL,
+ baseURLOverridden,
maxRetries = 2,
timeout = 60000, // 1 minute
httpAgent,
fetch: overriddenFetch,
}: {
baseURL: string;
+ baseURLOverridden: boolean;
maxRetries?: number | undefined;
timeout: number | undefined;
httpAgent: Agent | undefined;
fetch: Fetch | undefined;
}) {
this.baseURL = baseURL;
+ this.#baseURLOverridden = baseURLOverridden;
this.maxRetries = validatePositiveInteger('maxRetries', maxRetries);
this.timeout = validatePositiveInteger('timeout', timeout);
this.httpAgent = httpAgent;
@@ -194,7 +217,7 @@ export abstract class APIClient {
protected defaultHeaders(opts: FinalRequestOptions): Headers {
return {
Accept: 'application/json',
- 'Content-Type': 'application/json',
+ ...(['head', 'get'].includes(opts.method) ? {} : { 'Content-Type': 'application/json' }),
'User-Agent': this.getUserAgent(),
...getPlatformHeaders(),
...this.authHeaders(opts),
@@ -277,10 +300,11 @@ export abstract class APIClient {
}
buildRequest(
- options: FinalRequestOptions,
+ inputOptions: FinalRequestOptions,
{ retryCount = 0 }: { retryCount?: number } = {},
): { req: RequestInit; url: string; timeout: number } {
- const { method, path, query, headers: headers = {} } = options;
+ const options = { ...inputOptions };
+ const { method, path, query, defaultBaseURL, headers: headers = {} } = options;
const body =
ArrayBuffer.isView(options.body) || (options.__binaryRequest && typeof options.body === 'string') ?
@@ -290,11 +314,11 @@ export abstract class APIClient {
: null;
const contentLength = this.calculateContentLength(body);
- const url = this.buildURL(path!, query);
+ const url = this.buildURL(path!, query, defaultBaseURL);
if ('timeout' in options) validatePositiveInteger('timeout', options.timeout);
- const timeout = options.timeout ?? this.timeout;
+ options.timeout = options.timeout ?? this.timeout;
const httpAgent = options.httpAgent ?? this.httpAgent ?? getDefaultAgent(url);
- const minAgentTimeout = timeout + 1000;
+ const minAgentTimeout = options.timeout + 1000;
if (
typeof (httpAgent as any)?.options?.timeout === 'number' &&
minAgentTimeout > ((httpAgent as any).options.timeout ?? 0)
@@ -307,8 +331,8 @@ export abstract class APIClient {
}
if (this.idempotencyHeader && method !== 'get') {
- if (!options.idempotencyKey) options.idempotencyKey = this.defaultIdempotencyKey();
- headers[this.idempotencyHeader] = options.idempotencyKey;
+ if (!inputOptions.idempotencyKey) inputOptions.idempotencyKey = this.defaultIdempotencyKey();
+ headers[this.idempotencyHeader] = inputOptions.idempotencyKey;
}
const reqHeaders = this.buildHeaders({ options, headers, contentLength, retryCount });
@@ -323,7 +347,7 @@ export abstract class APIClient {
signal: options.signal ?? null,
};
- return { req, url, timeout };
+ return { req, url, timeout: options.timeout };
}
private buildHeaders({
@@ -351,15 +375,22 @@ export abstract class APIClient {
delete reqHeaders['content-type'];
}
- // Don't set the retry count header if it was already set or removed through default headers or by the
- // caller. We check `defaultHeaders` and `headers`, which can contain nulls, instead of `reqHeaders` to
- // account for the removal case.
+ // Don't set theses headers if they were already set or removed through default headers or by the caller.
+ // We check `defaultHeaders` and `headers`, which can contain nulls, instead of `reqHeaders` to account
+ // for the removal case.
if (
getHeader(defaultHeaders, 'x-stainless-retry-count') === undefined &&
getHeader(headers, 'x-stainless-retry-count') === undefined
) {
reqHeaders['x-stainless-retry-count'] = String(retryCount);
}
+ if (
+ getHeader(defaultHeaders, 'x-stainless-timeout') === undefined &&
+ getHeader(headers, 'x-stainless-timeout') === undefined &&
+ options.timeout
+ ) {
+ reqHeaders['x-stainless-timeout'] = String(Math.trunc(options.timeout / 1000));
+ }
this.validateHeaders(reqHeaders, headers);
@@ -387,7 +418,7 @@ export abstract class APIClient {
!headers ? {}
: Symbol.iterator in headers ?
Object.fromEntries(Array.from(headers as Iterable).map((header) => [...header]))
- : { ...headers }
+ : { ...(headers as any as Record) }
);
}
@@ -476,11 +507,12 @@ export abstract class APIClient {
return new PagePromise(this, request, Page);
}
- buildURL(path: string, query: Req | null | undefined): string {
+ buildURL(path: string, query: Req | null | undefined, defaultBaseURL?: string | undefined): string {
+ const baseURL = (!this.#baseURLOverridden && defaultBaseURL) || this.baseURL;
const url =
isAbsoluteURL(path) ?
new URL(path)
- : new URL(this.baseURL + (this.baseURL.endsWith('/') && path.startsWith('/') ? path.slice(1) : path));
+ : new URL(baseURL + (baseURL.endsWith('/') && path.startsWith('/') ? path.slice(1) : path));
const defaultQuery = this.defaultQuery();
if (!isEmptyObj(defaultQuery)) {
@@ -765,6 +797,7 @@ export type RequestOptions<
query?: Req | undefined;
body?: Req | null | undefined;
headers?: Headers | undefined;
+ defaultBaseURL?: string | undefined;
maxRetries?: number;
stream?: boolean | undefined;
@@ -786,6 +819,7 @@ const requestOptionsKeys: KeysEnum = {
query: true,
body: true,
headers: true,
+ defaultBaseURL: true,
maxRetries: true,
stream: true,
diff --git a/src/index.ts b/src/index.ts
index e2cc2f8..6775fcb 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -42,8 +42,10 @@ export interface ClientOptions {
*
* Note that request timeouts are retried by default, so in a worst-case scenario you may wait
* much longer than this timeout before the promise succeeds or fails.
+ *
+ * @unit milliseconds
*/
- timeout?: number;
+ timeout?: number | undefined;
/**
* An HTTP agent used to manage HTTP(S) connections.
@@ -51,7 +53,7 @@ export interface ClientOptions {
* If not provided, an agent will be constructed by default in the Node.js environment,
* otherwise no agent is used.
*/
- httpAgent?: Agent;
+ httpAgent?: Agent | undefined;
/**
* Specify a custom `fetch` function implementation.
@@ -67,7 +69,7 @@ export interface ClientOptions {
*
* @default 2
*/
- maxRetries?: number;
+ maxRetries?: number | undefined;
/**
* Default headers to include with every request to the API.
@@ -75,7 +77,7 @@ export interface ClientOptions {
* These can be removed in individual requests by explicitly setting the
* header to `undefined` or `null` in request options.
*/
- defaultHeaders?: Core.Headers;
+ defaultHeaders?: Core.Headers | undefined;
/**
* Default query parameters to include with every request to the API.
@@ -83,7 +85,7 @@ export interface ClientOptions {
* These can be removed in individual requests by explicitly setting the
* param to `undefined` in request options.
*/
- defaultQuery?: Core.DefaultQuery;
+ defaultQuery?: Core.DefaultQuery | undefined;
}
/**
@@ -119,6 +121,7 @@ export class Openlayer extends Core.APIClient {
super({
baseURL: options.baseURL!,
+ baseURLOverridden: baseURL ? baseURL !== 'https://api.openlayer.com/v1' : false,
timeout: options.timeout ?? 60000 /* 1 minute */,
httpAgent: options.httpAgent,
maxRetries: options.maxRetries,
@@ -135,6 +138,13 @@ export class Openlayer extends Core.APIClient {
inferencePipelines: API.InferencePipelines = new API.InferencePipelines(this);
storage: API.Storage = new API.Storage(this);
+ /**
+ * Check whether the base URL is set to its default.
+ */
+ #baseURLOverridden(): boolean {
+ return this.baseURL !== 'https://api.openlayer.com/v1';
+ }
+
protected override defaultQuery(): Core.DefaultQuery | undefined {
return this._options.defaultQuery;
}
diff --git a/src/resource.ts b/src/resource.ts
index 17ec80b..5014412 100644
--- a/src/resource.ts
+++ b/src/resource.ts
@@ -2,7 +2,7 @@
import type { Openlayer } from './index';
-export class APIResource {
+export abstract class APIResource {
protected _client: Openlayer;
constructor(client: Openlayer) {
diff --git a/src/resources.ts b/src/resources.ts
new file mode 100644
index 0000000..b283d57
--- /dev/null
+++ b/src/resources.ts
@@ -0,0 +1 @@
+export * from './resources/index';
diff --git a/src/resources/commits/test-results.ts b/src/resources/commits/test-results.ts
index ab1b977..e8a5d7a 100644
--- a/src/resources/commits/test-results.ts
+++ b/src/resources/commits/test-results.ts
@@ -140,7 +140,48 @@ export namespace TestResultListResponse {
/**
* The test subtype.
*/
- subtype: string;
+ subtype:
+ | 'anomalousColumnCount'
+ | 'characterLength'
+ | 'classImbalanceRatio'
+ | 'expectColumnAToBeInColumnB'
+ | 'columnAverage'
+ | 'columnDrift'
+ | 'columnStatistic'
+ | 'columnValuesMatch'
+ | 'conflictingLabelRowCount'
+ | 'containsPii'
+ | 'containsValidUrl'
+ | 'correlatedFeatureCount'
+ | 'customMetricThreshold'
+ | 'duplicateRowCount'
+ | 'emptyFeature'
+ | 'emptyFeatureCount'
+ | 'driftedFeatureCount'
+ | 'featureMissingValues'
+ | 'featureValueValidation'
+ | 'greatExpectations'
+ | 'groupByColumnStatsCheck'
+ | 'illFormedRowCount'
+ | 'isCode'
+ | 'isJson'
+ | 'llmRubricThresholdV2'
+ | 'labelDrift'
+ | 'metricThreshold'
+ | 'newCategoryCount'
+ | 'newLabelCount'
+ | 'nullRowCount'
+ | 'rowCount'
+ | 'ppScoreValueValidation'
+ | 'quasiConstantFeature'
+ | 'quasiConstantFeatureCount'
+ | 'sqlQuery'
+ | 'dtypeValidation'
+ | 'sentenceLength'
+ | 'sizeRatio'
+ | 'specialCharactersRatio'
+ | 'stringValidation'
+ | 'trainValLeakageRowCount';
/**
* Whether the test is suggested or user-created.
@@ -152,7 +193,7 @@ export namespace TestResultListResponse {
/**
* The test type.
*/
- type: string;
+ type: 'integrity' | 'consistency' | 'performance';
/**
* Whether the test is archived.
@@ -201,9 +242,48 @@ export namespace TestResultListResponse {
/**
* The insight name to be evaluated.
*/
- insightName?: string;
+ insightName?:
+ | 'characterLength'
+ | 'classImbalance'
+ | 'expectColumnAToBeInColumnB'
+ | 'columnAverage'
+ | 'columnDrift'
+ | 'columnValuesMatch'
+ | 'confidenceDistribution'
+ | 'conflictingLabelRowCount'
+ | 'containsPii'
+ | 'containsValidUrl'
+ | 'correlatedFeatures'
+ | 'customMetric'
+ | 'duplicateRowCount'
+ | 'emptyFeatures'
+ | 'featureDrift'
+ | 'featureProfile'
+ | 'greatExpectations'
+ | 'groupByColumnStatsCheck'
+ | 'illFormedRowCount'
+ | 'isCode'
+ | 'isJson'
+ | 'llmRubricV2'
+ | 'labelDrift'
+ | 'metrics'
+ | 'newCategories'
+ | 'newLabels'
+ | 'nullRowCount'
+ | 'ppScore'
+ | 'quasiConstantFeatures'
+ | 'sentenceLength'
+ | 'sizeRatio'
+ | 'specialCharacters'
+ | 'stringValidation'
+ | 'trainValLeakageRowCount';
- insightParameters?: Array;
+ /**
+ * The insight parameters. Required only for some test subtypes. For example, for
+ * tests that require a column name, the insight parameters will be [{'name':
+ * 'column_name', 'value': 'Age'}]
+ */
+ insightParameters?: Array | null;
/**
* The measurement to be evaluated.
@@ -213,20 +293,36 @@ export namespace TestResultListResponse {
/**
* The operator to be used for the evaluation.
*/
- operator?: string;
+ operator?: 'is' | '>' | '>=' | '<' | '<=' | '!=';
+
+ /**
+ * Whether to use automatic anomaly detection or manual thresholds
+ */
+ thresholdMode?: 'automatic' | 'manual';
/**
* The value to be compared.
*/
value?: number | boolean | string | Array;
}
+
+ export namespace Threshold {
+ export interface InsightParameter {
+ /**
+ * The name of the insight filter.
+ */
+ name: string;
+
+ value: unknown;
+ }
+ }
}
}
}
export interface TestResultListParams {
/**
- * Include archived goals.
+ * Filter for archived tests.
*/
includeArchived?: boolean;
diff --git a/src/resources/inference-pipelines/data.ts b/src/resources/inference-pipelines/data.ts
index d2009c3..e0110af 100644
--- a/src/resources/inference-pipelines/data.ts
+++ b/src/resources/inference-pipelines/data.ts
@@ -6,6 +6,26 @@ import * as Core from '../../core';
export class Data extends APIResource {
/**
* Publish an inference data point to an inference pipeline.
+ *
+ * @example
+ * ```ts
+ * const response =
+ * await client.inferencePipelines.data.stream(
+ * '182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e',
+ * {
+ * config: { outputColumnName: 'output' },
+ * rows: [
+ * {
+ * user_query: 'bar',
+ * output: 'bar',
+ * tokens: 'bar',
+ * cost: 'bar',
+ * timestamp: 'bar',
+ * },
+ * ],
+ * },
+ * );
+ * ```
*/
stream(
inferencePipelineId: string,
@@ -34,7 +54,7 @@ export interface DataStreamParams {
/**
* A list of inference data points with inputs and outputs
*/
- rows: Array>;
+ rows: Array<{ [key: string]: unknown }>;
}
export namespace DataStreamParams {
diff --git a/src/resources/inference-pipelines/inference-pipelines.ts b/src/resources/inference-pipelines/inference-pipelines.ts
index efdb59b..d50a161 100644
--- a/src/resources/inference-pipelines/inference-pipelines.ts
+++ b/src/resources/inference-pipelines/inference-pipelines.ts
@@ -17,6 +17,14 @@ export class InferencePipelines extends APIResource {
/**
* Retrieve inference pipeline.
+ *
+ * @example
+ * ```ts
+ * const inferencePipeline =
+ * await client.inferencePipelines.retrieve(
+ * '182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e',
+ * );
+ * ```
*/
retrieve(
inferencePipelineId: string,
@@ -40,6 +48,14 @@ export class InferencePipelines extends APIResource {
/**
* Update inference pipeline.
+ *
+ * @example
+ * ```ts
+ * const inferencePipeline =
+ * await client.inferencePipelines.update(
+ * '182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e',
+ * );
+ * ```
*/
update(
inferencePipelineId: string,
@@ -63,6 +79,13 @@ export class InferencePipelines extends APIResource {
/**
* Delete inference pipeline.
+ *
+ * @example
+ * ```ts
+ * await client.inferencePipelines.delete(
+ * '182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e',
+ * );
+ * ```
*/
delete(inferencePipelineId: string, options?: Core.RequestOptions): Core.APIPromise {
return this._client.delete(`/inference-pipelines/${inferencePipelineId}`, {
@@ -275,26 +298,59 @@ export namespace InferencePipelineRetrieveResponse {
}
export interface Workspace {
+ /**
+ * The workspace id.
+ */
id: string;
+ /**
+ * The workspace creator id.
+ */
creatorId: string | null;
+ /**
+ * The workspace creation date.
+ */
dateCreated: string;
+ /**
+ * The workspace last updated date.
+ */
dateUpdated: string;
+ /**
+ * The number of invites in the workspace.
+ */
inviteCount: number;
+ /**
+ * The number of members in the workspace.
+ */
memberCount: number;
+ /**
+ * The workspace name.
+ */
name: string;
+ /**
+ * The end date of the current billing period.
+ */
periodEndDate: string | null;
+ /**
+ * The start date of the current billing period.
+ */
periodStartDate: string | null;
+ /**
+ * The number of projects in the workspace.
+ */
projectCount: number;
+ /**
+ * The workspace slug.
+ */
slug: string;
status:
@@ -309,6 +365,9 @@ export namespace InferencePipelineRetrieveResponse {
monthlyUsage?: Array;
+ /**
+ * Whether the workspace only allows SAML authentication.
+ */
samlOnlyAccess?: boolean;
wildcardDomains?: Array;
@@ -528,26 +587,59 @@ export namespace InferencePipelineUpdateResponse {
}
export interface Workspace {
+ /**
+ * The workspace id.
+ */
id: string;
+ /**
+ * The workspace creator id.
+ */
creatorId: string | null;
+ /**
+ * The workspace creation date.
+ */
dateCreated: string;
+ /**
+ * The workspace last updated date.
+ */
dateUpdated: string;
+ /**
+ * The number of invites in the workspace.
+ */
inviteCount: number;
+ /**
+ * The number of members in the workspace.
+ */
memberCount: number;
+ /**
+ * The workspace name.
+ */
name: string;
+ /**
+ * The end date of the current billing period.
+ */
periodEndDate: string | null;
+ /**
+ * The start date of the current billing period.
+ */
periodStartDate: string | null;
+ /**
+ * The number of projects in the workspace.
+ */
projectCount: number;
+ /**
+ * The workspace slug.
+ */
slug: string;
status:
@@ -562,6 +654,9 @@ export namespace InferencePipelineUpdateResponse {
monthlyUsage?: Array;
+ /**
+ * Whether the workspace only allows SAML authentication.
+ */
samlOnlyAccess?: boolean;
wildcardDomains?: Array;
diff --git a/src/resources/inference-pipelines/rows.ts b/src/resources/inference-pipelines/rows.ts
index b959d41..6aebd28 100644
--- a/src/resources/inference-pipelines/rows.ts
+++ b/src/resources/inference-pipelines/rows.ts
@@ -6,6 +6,14 @@ import * as Core from '../../core';
export class Rows extends APIResource {
/**
* Update an inference data point in an inference pipeline.
+ *
+ * @example
+ * ```ts
+ * const row = await client.inferencePipelines.rows.update(
+ * '182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e',
+ * { inferenceId: 'inferenceId', row: {} },
+ * );
+ * ```
*/
update(
inferencePipelineId: string,
diff --git a/src/resources/inference-pipelines/test-results.ts b/src/resources/inference-pipelines/test-results.ts
index 17a72f9..fad5c0f 100644
--- a/src/resources/inference-pipelines/test-results.ts
+++ b/src/resources/inference-pipelines/test-results.ts
@@ -7,6 +7,14 @@ import * as Core from '../../core';
export class TestResults extends APIResource {
/**
* List the latest test results for an inference pipeline.
+ *
+ * @example
+ * ```ts
+ * const testResults =
+ * await client.inferencePipelines.testResults.list(
+ * '182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e',
+ * );
+ * ```
*/
list(
inferencePipelineId: string,
@@ -140,7 +148,48 @@ export namespace TestResultListResponse {
/**
* The test subtype.
*/
- subtype: string;
+ subtype:
+ | 'anomalousColumnCount'
+ | 'characterLength'
+ | 'classImbalanceRatio'
+ | 'expectColumnAToBeInColumnB'
+ | 'columnAverage'
+ | 'columnDrift'
+ | 'columnStatistic'
+ | 'columnValuesMatch'
+ | 'conflictingLabelRowCount'
+ | 'containsPii'
+ | 'containsValidUrl'
+ | 'correlatedFeatureCount'
+ | 'customMetricThreshold'
+ | 'duplicateRowCount'
+ | 'emptyFeature'
+ | 'emptyFeatureCount'
+ | 'driftedFeatureCount'
+ | 'featureMissingValues'
+ | 'featureValueValidation'
+ | 'greatExpectations'
+ | 'groupByColumnStatsCheck'
+ | 'illFormedRowCount'
+ | 'isCode'
+ | 'isJson'
+ | 'llmRubricThresholdV2'
+ | 'labelDrift'
+ | 'metricThreshold'
+ | 'newCategoryCount'
+ | 'newLabelCount'
+ | 'nullRowCount'
+ | 'rowCount'
+ | 'ppScoreValueValidation'
+ | 'quasiConstantFeature'
+ | 'quasiConstantFeatureCount'
+ | 'sqlQuery'
+ | 'dtypeValidation'
+ | 'sentenceLength'
+ | 'sizeRatio'
+ | 'specialCharactersRatio'
+ | 'stringValidation'
+ | 'trainValLeakageRowCount';
/**
* Whether the test is suggested or user-created.
@@ -152,7 +201,7 @@ export namespace TestResultListResponse {
/**
* The test type.
*/
- type: string;
+ type: 'integrity' | 'consistency' | 'performance';
/**
* Whether the test is archived.
@@ -201,9 +250,48 @@ export namespace TestResultListResponse {
/**
* The insight name to be evaluated.
*/
- insightName?: string;
+ insightName?:
+ | 'characterLength'
+ | 'classImbalance'
+ | 'expectColumnAToBeInColumnB'
+ | 'columnAverage'
+ | 'columnDrift'
+ | 'columnValuesMatch'
+ | 'confidenceDistribution'
+ | 'conflictingLabelRowCount'
+ | 'containsPii'
+ | 'containsValidUrl'
+ | 'correlatedFeatures'
+ | 'customMetric'
+ | 'duplicateRowCount'
+ | 'emptyFeatures'
+ | 'featureDrift'
+ | 'featureProfile'
+ | 'greatExpectations'
+ | 'groupByColumnStatsCheck'
+ | 'illFormedRowCount'
+ | 'isCode'
+ | 'isJson'
+ | 'llmRubricV2'
+ | 'labelDrift'
+ | 'metrics'
+ | 'newCategories'
+ | 'newLabels'
+ | 'nullRowCount'
+ | 'ppScore'
+ | 'quasiConstantFeatures'
+ | 'sentenceLength'
+ | 'sizeRatio'
+ | 'specialCharacters'
+ | 'stringValidation'
+ | 'trainValLeakageRowCount';
- insightParameters?: Array;
+ /**
+ * The insight parameters. Required only for some test subtypes. For example, for
+ * tests that require a column name, the insight parameters will be [{'name':
+ * 'column_name', 'value': 'Age'}]
+ */
+ insightParameters?: Array | null;
/**
* The measurement to be evaluated.
@@ -213,13 +301,29 @@ export namespace TestResultListResponse {
/**
* The operator to be used for the evaluation.
*/
- operator?: string;
+ operator?: 'is' | '>' | '>=' | '<' | '<=' | '!=';
+
+ /**
+ * Whether to use automatic anomaly detection or manual thresholds
+ */
+ thresholdMode?: 'automatic' | 'manual';
/**
* The value to be compared.
*/
value?: number | boolean | string | Array;
}
+
+ export namespace Threshold {
+ export interface InsightParameter {
+ /**
+ * The name of the insight filter.
+ */
+ name: string;
+
+ value: unknown;
+ }
+ }
}
}
}
diff --git a/src/resources/projects/commits.ts b/src/resources/projects/commits.ts
index 8736852..cacf749 100644
--- a/src/resources/projects/commits.ts
+++ b/src/resources/projects/commits.ts
@@ -7,6 +7,17 @@ import * as Core from '../../core';
export class Commits extends APIResource {
/**
* Create a new commit (project version) in a project.
+ *
+ * @example
+ * ```ts
+ * const commit = await client.projects.commits.create(
+ * '182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e',
+ * {
+ * commit: { message: 'Updated the prompt.' },
+ * storageUri: 's3://...',
+ * },
+ * );
+ * ```
*/
create(
projectId: string,
@@ -18,6 +29,13 @@ export class Commits extends APIResource {
/**
* List the commits (project versions) in a project.
+ *
+ * @example
+ * ```ts
+ * const commits = await client.projects.commits.list(
+ * '182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e',
+ * );
+ * ```
*/
list(
projectId: string,
diff --git a/src/resources/projects/index.ts b/src/resources/projects/index.ts
index de97190..c53d21e 100644
--- a/src/resources/projects/index.ts
+++ b/src/resources/projects/index.ts
@@ -21,3 +21,12 @@ export {
type ProjectCreateParams,
type ProjectListParams,
} from './projects';
+export {
+ Tests,
+ type TestCreateResponse,
+ type TestUpdateResponse,
+ type TestListResponse,
+ type TestCreateParams,
+ type TestUpdateParams,
+ type TestListParams,
+} from './tests';
diff --git a/src/resources/projects/inference-pipelines.ts b/src/resources/projects/inference-pipelines.ts
index 7f91d92..b46c7af 100644
--- a/src/resources/projects/inference-pipelines.ts
+++ b/src/resources/projects/inference-pipelines.ts
@@ -7,6 +7,18 @@ import * as Core from '../../core';
export class InferencePipelines extends APIResource {
/**
* Create an inference pipeline in a project.
+ *
+ * @example
+ * ```ts
+ * const inferencePipeline =
+ * await client.projects.inferencePipelines.create(
+ * '182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e',
+ * {
+ * description: 'This pipeline is used for production.',
+ * name: 'production',
+ * },
+ * );
+ * ```
*/
create(
projectId: string,
@@ -18,6 +30,14 @@ export class InferencePipelines extends APIResource {
/**
* List the inference pipelines in a project.
+ *
+ * @example
+ * ```ts
+ * const inferencePipelines =
+ * await client.projects.inferencePipelines.list(
+ * '182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e',
+ * );
+ * ```
*/
list(
projectId: string,
@@ -240,26 +260,59 @@ export namespace InferencePipelineCreateResponse {
}
export interface Workspace {
+ /**
+ * The workspace id.
+ */
id: string;
+ /**
+ * The workspace creator id.
+ */
creatorId: string | null;
+ /**
+ * The workspace creation date.
+ */
dateCreated: string;
+ /**
+ * The workspace last updated date.
+ */
dateUpdated: string;
+ /**
+ * The number of invites in the workspace.
+ */
inviteCount: number;
+ /**
+ * The number of members in the workspace.
+ */
memberCount: number;
+ /**
+ * The workspace name.
+ */
name: string;
+ /**
+ * The end date of the current billing period.
+ */
periodEndDate: string | null;
+ /**
+ * The start date of the current billing period.
+ */
periodStartDate: string | null;
+ /**
+ * The number of projects in the workspace.
+ */
projectCount: number;
+ /**
+ * The workspace slug.
+ */
slug: string;
status:
@@ -274,6 +327,9 @@ export namespace InferencePipelineCreateResponse {
monthlyUsage?: Array;
+ /**
+ * Whether the workspace only allows SAML authentication.
+ */
samlOnlyAccess?: boolean;
wildcardDomains?: Array;
@@ -498,26 +554,59 @@ export namespace InferencePipelineListResponse {
}
export interface Workspace {
+ /**
+ * The workspace id.
+ */
id: string;
+ /**
+ * The workspace creator id.
+ */
creatorId: string | null;
+ /**
+ * The workspace creation date.
+ */
dateCreated: string;
+ /**
+ * The workspace last updated date.
+ */
dateUpdated: string;
+ /**
+ * The number of invites in the workspace.
+ */
inviteCount: number;
+ /**
+ * The number of members in the workspace.
+ */
memberCount: number;
+ /**
+ * The workspace name.
+ */
name: string;
+ /**
+ * The end date of the current billing period.
+ */
periodEndDate: string | null;
+ /**
+ * The start date of the current billing period.
+ */
periodStartDate: string | null;
+ /**
+ * The number of projects in the workspace.
+ */
projectCount: number;
+ /**
+ * The workspace slug.
+ */
slug: string;
status:
@@ -532,6 +621,9 @@ export namespace InferencePipelineListResponse {
monthlyUsage?: Array;
+ /**
+ * Whether the workspace only allows SAML authentication.
+ */
samlOnlyAccess?: boolean;
wildcardDomains?: Array;
@@ -584,12 +676,24 @@ export namespace InferencePipelineCreateParams {
}
export interface Workspace {
+ /**
+ * The workspace name.
+ */
name: string;
+ /**
+ * The workspace slug.
+ */
slug: string;
+ /**
+ * The workspace invite code.
+ */
inviteCode?: string;
+ /**
+ * Whether the workspace only allows SAML authentication.
+ */
samlOnlyAccess?: boolean;
wildcardDomains?: Array;
diff --git a/src/resources/projects/projects.ts b/src/resources/projects/projects.ts
index dbf39e5..15428f6 100644
--- a/src/resources/projects/projects.ts
+++ b/src/resources/projects/projects.ts
@@ -19,15 +19,34 @@ import {
InferencePipelineListResponse,
InferencePipelines,
} from './inference-pipelines';
+import * as TestsAPI from './tests';
+import {
+ TestCreateParams,
+ TestCreateResponse,
+ TestListParams,
+ TestListResponse,
+ TestUpdateParams,
+ TestUpdateResponse,
+ Tests,
+} from './tests';
export class Projects extends APIResource {
commits: CommitsAPI.Commits = new CommitsAPI.Commits(this._client);
inferencePipelines: InferencePipelinesAPI.InferencePipelines = new InferencePipelinesAPI.InferencePipelines(
this._client,
);
+ tests: TestsAPI.Tests = new TestsAPI.Tests(this._client);
/**
* Create a project in your workspace.
+ *
+ * @example
+ * ```ts
+ * const project = await client.projects.create({
+ * name: 'My Project',
+ * taskType: 'llm-base',
+ * });
+ * ```
*/
create(body: ProjectCreateParams, options?: Core.RequestOptions): Core.APIPromise {
return this._client.post('/projects', { body, ...options });
@@ -35,6 +54,11 @@ export class Projects extends APIResource {
/**
* List your workspace's projects.
+ *
+ * @example
+ * ```ts
+ * const projects = await client.projects.list();
+ * ```
*/
list(query?: ProjectListParams, options?: Core.RequestOptions): Core.APIPromise;
list(options?: Core.RequestOptions): Core.APIPromise;
@@ -324,6 +348,7 @@ export interface ProjectListParams {
Projects.Commits = Commits;
Projects.InferencePipelines = InferencePipelines;
+Projects.Tests = Tests;
export declare namespace Projects {
export {
@@ -348,4 +373,14 @@ export declare namespace Projects {
type InferencePipelineCreateParams as InferencePipelineCreateParams,
type InferencePipelineListParams as InferencePipelineListParams,
};
+
+ export {
+ Tests as Tests,
+ type TestCreateResponse as TestCreateResponse,
+ type TestUpdateResponse as TestUpdateResponse,
+ type TestListResponse as TestListResponse,
+ type TestCreateParams as TestCreateParams,
+ type TestUpdateParams as TestUpdateParams,
+ type TestListParams as TestListParams,
+ };
}
diff --git a/src/resources/projects/tests.ts b/src/resources/projects/tests.ts
new file mode 100644
index 0000000..a50048b
--- /dev/null
+++ b/src/resources/projects/tests.ts
@@ -0,0 +1,905 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIResource } from '../../resource';
+import { isRequestOptions } from '../../core';
+import * as Core from '../../core';
+
+export class Tests extends APIResource {
+ /**
+ * Create a test.
+ *
+ * @example
+ * ```ts
+ * const test = await client.projects.tests.create(
+ * '182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e',
+ * {
+ * description:
+ * 'This test checks for duplicate rows in the dataset.',
+ * name: 'No duplicate rows',
+ * subtype: 'duplicateRowCount',
+ * thresholds: [{}],
+ * type: 'integrity',
+ * },
+ * );
+ * ```
+ */
+ create(
+ projectId: string,
+ body: TestCreateParams,
+ options?: Core.RequestOptions,
+ ): Core.APIPromise {
+ return this._client.post(`/projects/${projectId}/tests`, { body, ...options });
+ }
+
+ /**
+ * Update tests.
+ *
+ * @example
+ * ```ts
+ * const test = await client.projects.tests.update(
+ * '182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e',
+ * {
+ * payloads: [
+ * { id: '182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e' },
+ * ],
+ * },
+ * );
+ * ```
+ */
+ update(
+ projectId: string,
+ body: TestUpdateParams,
+ options?: Core.RequestOptions,
+ ): Core.APIPromise {
+ return this._client.put(`/projects/${projectId}/tests`, { body, ...options });
+ }
+
+ /**
+ * List tests under a project.
+ *
+ * @example
+ * ```ts
+ * const tests = await client.projects.tests.list(
+ * '182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e',
+ * );
+ * ```
+ */
+ list(
+ projectId: string,
+ query?: TestListParams,
+ options?: Core.RequestOptions,
+ ): Core.APIPromise;
+ list(projectId: string, options?: Core.RequestOptions): Core.APIPromise;
+ list(
+ projectId: string,
+ query: TestListParams | Core.RequestOptions = {},
+ options?: Core.RequestOptions,
+ ): Core.APIPromise {
+ if (isRequestOptions(query)) {
+ return this.list(projectId, {}, query);
+ }
+ return this._client.get(`/projects/${projectId}/tests`, { query, ...options });
+ }
+}
+
+export interface TestCreateResponse {
+ /**
+ * The test id.
+ */
+ id: string;
+
+ /**
+ * The number of comments on the test.
+ */
+ commentCount: number;
+
+ /**
+ * The test creator id.
+ */
+ creatorId: string | null;
+
+ /**
+ * The date the test was archived.
+ */
+ dateArchived: string | null;
+
+ /**
+ * The creation date.
+ */
+ dateCreated: string;
+
+ /**
+ * The last updated date.
+ */
+ dateUpdated: string;
+
+ /**
+ * The test description.
+ */
+ description: unknown | null;
+
+ /**
+ * The test name.
+ */
+ name: string;
+
+ /**
+ * The test number.
+ */
+ number: number;
+
+ /**
+ * The project version (commit) id where the test was created.
+ */
+ originProjectVersionId: string | null;
+
+ /**
+ * The test subtype.
+ */
+ subtype:
+ | 'anomalousColumnCount'
+ | 'characterLength'
+ | 'classImbalanceRatio'
+ | 'expectColumnAToBeInColumnB'
+ | 'columnAverage'
+ | 'columnDrift'
+ | 'columnStatistic'
+ | 'columnValuesMatch'
+ | 'conflictingLabelRowCount'
+ | 'containsPii'
+ | 'containsValidUrl'
+ | 'correlatedFeatureCount'
+ | 'customMetricThreshold'
+ | 'duplicateRowCount'
+ | 'emptyFeature'
+ | 'emptyFeatureCount'
+ | 'driftedFeatureCount'
+ | 'featureMissingValues'
+ | 'featureValueValidation'
+ | 'greatExpectations'
+ | 'groupByColumnStatsCheck'
+ | 'illFormedRowCount'
+ | 'isCode'
+ | 'isJson'
+ | 'llmRubricThresholdV2'
+ | 'labelDrift'
+ | 'metricThreshold'
+ | 'newCategoryCount'
+ | 'newLabelCount'
+ | 'nullRowCount'
+ | 'rowCount'
+ | 'ppScoreValueValidation'
+ | 'quasiConstantFeature'
+ | 'quasiConstantFeatureCount'
+ | 'sqlQuery'
+ | 'dtypeValidation'
+ | 'sentenceLength'
+ | 'sizeRatio'
+ | 'specialCharactersRatio'
+ | 'stringValidation'
+ | 'trainValLeakageRowCount';
+
+ /**
+ * Whether the test is suggested or user-created.
+ */
+ suggested: boolean;
+
+ thresholds: Array;
+
+ /**
+ * The test type.
+ */
+ type: 'integrity' | 'consistency' | 'performance';
+
+ /**
+ * Whether the test is archived.
+ */
+ archived?: boolean;
+
+ /**
+ * The delay window in seconds. Only applies to tests that use production data.
+ */
+ delayWindow?: number | null;
+
+ /**
+ * The evaluation window in seconds. Only applies to tests that use production
+ * data.
+ */
+ evaluationWindow?: number | null;
+
+ /**
+ * Whether the test uses an ML model.
+ */
+ usesMlModel?: boolean;
+
+ /**
+ * Whether the test uses production data (monitoring mode only).
+ */
+ usesProductionData?: boolean;
+
+ /**
+ * Whether the test uses a reference dataset (monitoring mode only).
+ */
+ usesReferenceDataset?: boolean;
+
+ /**
+ * Whether the test uses a training dataset.
+ */
+ usesTrainingDataset?: boolean;
+
+ /**
+ * Whether the test uses a validation dataset.
+ */
+ usesValidationDataset?: boolean;
+}
+
+export namespace TestCreateResponse {
+ export interface Threshold {
+ /**
+ * The insight name to be evaluated.
+ */
+ insightName?:
+ | 'characterLength'
+ | 'classImbalance'
+ | 'expectColumnAToBeInColumnB'
+ | 'columnAverage'
+ | 'columnDrift'
+ | 'columnValuesMatch'
+ | 'confidenceDistribution'
+ | 'conflictingLabelRowCount'
+ | 'containsPii'
+ | 'containsValidUrl'
+ | 'correlatedFeatures'
+ | 'customMetric'
+ | 'duplicateRowCount'
+ | 'emptyFeatures'
+ | 'featureDrift'
+ | 'featureProfile'
+ | 'greatExpectations'
+ | 'groupByColumnStatsCheck'
+ | 'illFormedRowCount'
+ | 'isCode'
+ | 'isJson'
+ | 'llmRubricV2'
+ | 'labelDrift'
+ | 'metrics'
+ | 'newCategories'
+ | 'newLabels'
+ | 'nullRowCount'
+ | 'ppScore'
+ | 'quasiConstantFeatures'
+ | 'sentenceLength'
+ | 'sizeRatio'
+ | 'specialCharacters'
+ | 'stringValidation'
+ | 'trainValLeakageRowCount';
+
+ /**
+ * The insight parameters. Required only for some test subtypes. For example, for
+ * tests that require a column name, the insight parameters will be [{'name':
+ * 'column_name', 'value': 'Age'}]
+ */
+ insightParameters?: Array | null;
+
+ /**
+ * The measurement to be evaluated.
+ */
+ measurement?: string;
+
+ /**
+ * The operator to be used for the evaluation.
+ */
+ operator?: 'is' | '>' | '>=' | '<' | '<=' | '!=';
+
+ /**
+ * Whether to use automatic anomaly detection or manual thresholds
+ */
+ thresholdMode?: 'automatic' | 'manual';
+
+ /**
+ * The value to be compared.
+ */
+ value?: number | boolean | string | Array;
+ }
+
+ export namespace Threshold {
+ export interface InsightParameter {
+ /**
+ * The name of the insight filter.
+ */
+ name: string;
+
+ value: unknown;
+ }
+ }
+}
+
+export interface TestUpdateResponse {
+ taskResultId?: string;
+
+ taskResultUrl?: string;
+}
+
+export interface TestListResponse {
+ items: Array;
+}
+
+export namespace TestListResponse {
+ export interface Item {
+ /**
+ * The test id.
+ */
+ id: string;
+
+ /**
+ * The number of comments on the test.
+ */
+ commentCount: number;
+
+ /**
+ * The test creator id.
+ */
+ creatorId: string | null;
+
+ /**
+ * The date the test was archived.
+ */
+ dateArchived: string | null;
+
+ /**
+ * The creation date.
+ */
+ dateCreated: string;
+
+ /**
+ * The last updated date.
+ */
+ dateUpdated: string;
+
+ /**
+ * The test description.
+ */
+ description: unknown | null;
+
+ /**
+ * The test name.
+ */
+ name: string;
+
+ /**
+ * The test number.
+ */
+ number: number;
+
+ /**
+ * The project version (commit) id where the test was created.
+ */
+ originProjectVersionId: string | null;
+
+ /**
+ * The test subtype.
+ */
+ subtype:
+ | 'anomalousColumnCount'
+ | 'characterLength'
+ | 'classImbalanceRatio'
+ | 'expectColumnAToBeInColumnB'
+ | 'columnAverage'
+ | 'columnDrift'
+ | 'columnStatistic'
+ | 'columnValuesMatch'
+ | 'conflictingLabelRowCount'
+ | 'containsPii'
+ | 'containsValidUrl'
+ | 'correlatedFeatureCount'
+ | 'customMetricThreshold'
+ | 'duplicateRowCount'
+ | 'emptyFeature'
+ | 'emptyFeatureCount'
+ | 'driftedFeatureCount'
+ | 'featureMissingValues'
+ | 'featureValueValidation'
+ | 'greatExpectations'
+ | 'groupByColumnStatsCheck'
+ | 'illFormedRowCount'
+ | 'isCode'
+ | 'isJson'
+ | 'llmRubricThresholdV2'
+ | 'labelDrift'
+ | 'metricThreshold'
+ | 'newCategoryCount'
+ | 'newLabelCount'
+ | 'nullRowCount'
+ | 'rowCount'
+ | 'ppScoreValueValidation'
+ | 'quasiConstantFeature'
+ | 'quasiConstantFeatureCount'
+ | 'sqlQuery'
+ | 'dtypeValidation'
+ | 'sentenceLength'
+ | 'sizeRatio'
+ | 'specialCharactersRatio'
+ | 'stringValidation'
+ | 'trainValLeakageRowCount';
+
+ /**
+ * Whether the test is suggested or user-created.
+ */
+ suggested: boolean;
+
+ thresholds: Array;
+
+ /**
+ * The test type.
+ */
+ type: 'integrity' | 'consistency' | 'performance';
+
+ /**
+ * Whether the test is archived.
+ */
+ archived?: boolean;
+
+ /**
+ * The delay window in seconds. Only applies to tests that use production data.
+ */
+ delayWindow?: number | null;
+
+ /**
+ * The evaluation window in seconds. Only applies to tests that use production
+ * data.
+ */
+ evaluationWindow?: number | null;
+
+ /**
+ * Whether the test uses an ML model.
+ */
+ usesMlModel?: boolean;
+
+ /**
+ * Whether the test uses production data (monitoring mode only).
+ */
+ usesProductionData?: boolean;
+
+ /**
+ * Whether the test uses a reference dataset (monitoring mode only).
+ */
+ usesReferenceDataset?: boolean;
+
+ /**
+ * Whether the test uses a training dataset.
+ */
+ usesTrainingDataset?: boolean;
+
+ /**
+ * Whether the test uses a validation dataset.
+ */
+ usesValidationDataset?: boolean;
+ }
+
+ export namespace Item {
+ export interface Threshold {
+ /**
+ * The insight name to be evaluated.
+ */
+ insightName?:
+ | 'characterLength'
+ | 'classImbalance'
+ | 'expectColumnAToBeInColumnB'
+ | 'columnAverage'
+ | 'columnDrift'
+ | 'columnValuesMatch'
+ | 'confidenceDistribution'
+ | 'conflictingLabelRowCount'
+ | 'containsPii'
+ | 'containsValidUrl'
+ | 'correlatedFeatures'
+ | 'customMetric'
+ | 'duplicateRowCount'
+ | 'emptyFeatures'
+ | 'featureDrift'
+ | 'featureProfile'
+ | 'greatExpectations'
+ | 'groupByColumnStatsCheck'
+ | 'illFormedRowCount'
+ | 'isCode'
+ | 'isJson'
+ | 'llmRubricV2'
+ | 'labelDrift'
+ | 'metrics'
+ | 'newCategories'
+ | 'newLabels'
+ | 'nullRowCount'
+ | 'ppScore'
+ | 'quasiConstantFeatures'
+ | 'sentenceLength'
+ | 'sizeRatio'
+ | 'specialCharacters'
+ | 'stringValidation'
+ | 'trainValLeakageRowCount';
+
+ /**
+ * The insight parameters. Required only for some test subtypes. For example, for
+ * tests that require a column name, the insight parameters will be [{'name':
+ * 'column_name', 'value': 'Age'}]
+ */
+ insightParameters?: Array | null;
+
+ /**
+ * The measurement to be evaluated.
+ */
+ measurement?: string;
+
+ /**
+ * The operator to be used for the evaluation.
+ */
+ operator?: 'is' | '>' | '>=' | '<' | '<=' | '!=';
+
+ /**
+ * Whether to use automatic anomaly detection or manual thresholds
+ */
+ thresholdMode?: 'automatic' | 'manual';
+
+ /**
+ * The value to be compared.
+ */
+ value?: number | boolean | string | Array;
+ }
+
+ export namespace Threshold {
+ export interface InsightParameter {
+ /**
+ * The name of the insight filter.
+ */
+ name: string;
+
+ value: unknown;
+ }
+ }
+ }
+}
+
+export interface TestCreateParams {
+ /**
+ * The test description.
+ */
+ description: unknown | null;
+
+ /**
+ * The test name.
+ */
+ name: string;
+
+ /**
+ * The test subtype.
+ */
+ subtype:
+ | 'anomalousColumnCount'
+ | 'characterLength'
+ | 'classImbalanceRatio'
+ | 'expectColumnAToBeInColumnB'
+ | 'columnAverage'
+ | 'columnDrift'
+ | 'columnStatistic'
+ | 'columnValuesMatch'
+ | 'conflictingLabelRowCount'
+ | 'containsPii'
+ | 'containsValidUrl'
+ | 'correlatedFeatureCount'
+ | 'customMetricThreshold'
+ | 'duplicateRowCount'
+ | 'emptyFeature'
+ | 'emptyFeatureCount'
+ | 'driftedFeatureCount'
+ | 'featureMissingValues'
+ | 'featureValueValidation'
+ | 'greatExpectations'
+ | 'groupByColumnStatsCheck'
+ | 'illFormedRowCount'
+ | 'isCode'
+ | 'isJson'
+ | 'llmRubricThresholdV2'
+ | 'labelDrift'
+ | 'metricThreshold'
+ | 'newCategoryCount'
+ | 'newLabelCount'
+ | 'nullRowCount'
+ | 'rowCount'
+ | 'ppScoreValueValidation'
+ | 'quasiConstantFeature'
+ | 'quasiConstantFeatureCount'
+ | 'sqlQuery'
+ | 'dtypeValidation'
+ | 'sentenceLength'
+ | 'sizeRatio'
+ | 'specialCharactersRatio'
+ | 'stringValidation'
+ | 'trainValLeakageRowCount';
+
+ thresholds: Array;
+
+ /**
+ * The test type.
+ */
+ type: 'integrity' | 'consistency' | 'performance';
+
+ /**
+ * Whether the test is archived.
+ */
+ archived?: boolean;
+
+ /**
+ * The delay window in seconds. Only applies to tests that use production data.
+ */
+ delayWindow?: number | null;
+
+ /**
+ * The evaluation window in seconds. Only applies to tests that use production
+ * data.
+ */
+ evaluationWindow?: number | null;
+
+ /**
+ * Whether the test uses an ML model.
+ */
+ usesMlModel?: boolean;
+
+ /**
+ * Whether the test uses production data (monitoring mode only).
+ */
+ usesProductionData?: boolean;
+
+ /**
+ * Whether the test uses a reference dataset (monitoring mode only).
+ */
+ usesReferenceDataset?: boolean;
+
+ /**
+ * Whether the test uses a training dataset.
+ */
+ usesTrainingDataset?: boolean;
+
+ /**
+ * Whether the test uses a validation dataset.
+ */
+ usesValidationDataset?: boolean;
+}
+
+export namespace TestCreateParams {
+ export interface Threshold {
+ /**
+ * The insight name to be evaluated.
+ */
+ insightName?:
+ | 'characterLength'
+ | 'classImbalance'
+ | 'expectColumnAToBeInColumnB'
+ | 'columnAverage'
+ | 'columnDrift'
+ | 'columnValuesMatch'
+ | 'confidenceDistribution'
+ | 'conflictingLabelRowCount'
+ | 'containsPii'
+ | 'containsValidUrl'
+ | 'correlatedFeatures'
+ | 'customMetric'
+ | 'duplicateRowCount'
+ | 'emptyFeatures'
+ | 'featureDrift'
+ | 'featureProfile'
+ | 'greatExpectations'
+ | 'groupByColumnStatsCheck'
+ | 'illFormedRowCount'
+ | 'isCode'
+ | 'isJson'
+ | 'llmRubricV2'
+ | 'labelDrift'
+ | 'metrics'
+ | 'newCategories'
+ | 'newLabels'
+ | 'nullRowCount'
+ | 'ppScore'
+ | 'quasiConstantFeatures'
+ | 'sentenceLength'
+ | 'sizeRatio'
+ | 'specialCharacters'
+ | 'stringValidation'
+ | 'trainValLeakageRowCount';
+
+ /**
+ * The insight parameters. Required only for some test subtypes. For example, for
+ * tests that require a column name, the insight parameters will be [{'name':
+ * 'column_name', 'value': 'Age'}]
+ */
+ insightParameters?: Array | null;
+
+ /**
+ * The measurement to be evaluated.
+ */
+ measurement?: string;
+
+ /**
+ * The operator to be used for the evaluation.
+ */
+ operator?: 'is' | '>' | '>=' | '<' | '<=' | '!=';
+
+ /**
+ * Whether to use automatic anomaly detection or manual thresholds
+ */
+ thresholdMode?: 'automatic' | 'manual';
+
+ /**
+ * The value to be compared.
+ */
+ value?: number | boolean | string | Array;
+ }
+
+ export namespace Threshold {
+ export interface InsightParameter {
+ /**
+ * The name of the insight filter.
+ */
+ name: string;
+
+ value: unknown;
+ }
+ }
+}
+
+export interface TestUpdateParams {
+ payloads: Array;
+}
+
+export namespace TestUpdateParams {
+ export interface Payload {
+ id: string;
+
+ /**
+ * Whether the test is archived.
+ */
+ archived?: boolean;
+
+ /**
+ * The test description.
+ */
+ description?: unknown | null;
+
+ /**
+ * The test name.
+ */
+ name?: string;
+
+ suggested?: false;
+
+ thresholds?: Array;
+ }
+
+ export namespace Payload {
+ export interface Threshold {
+ /**
+ * The insight name to be evaluated.
+ */
+ insightName?:
+ | 'characterLength'
+ | 'classImbalance'
+ | 'expectColumnAToBeInColumnB'
+ | 'columnAverage'
+ | 'columnDrift'
+ | 'columnValuesMatch'
+ | 'confidenceDistribution'
+ | 'conflictingLabelRowCount'
+ | 'containsPii'
+ | 'containsValidUrl'
+ | 'correlatedFeatures'
+ | 'customMetric'
+ | 'duplicateRowCount'
+ | 'emptyFeatures'
+ | 'featureDrift'
+ | 'featureProfile'
+ | 'greatExpectations'
+ | 'groupByColumnStatsCheck'
+ | 'illFormedRowCount'
+ | 'isCode'
+ | 'isJson'
+ | 'llmRubricV2'
+ | 'labelDrift'
+ | 'metrics'
+ | 'newCategories'
+ | 'newLabels'
+ | 'nullRowCount'
+ | 'ppScore'
+ | 'quasiConstantFeatures'
+ | 'sentenceLength'
+ | 'sizeRatio'
+ | 'specialCharacters'
+ | 'stringValidation'
+ | 'trainValLeakageRowCount';
+
+ /**
+ * The insight parameters. Required only for some test subtypes. For example, for
+ * tests that require a column name, the insight parameters will be [{'name':
+ * 'column_name', 'value': 'Age'}]
+ */
+ insightParameters?: Array | null;
+
+ /**
+ * The measurement to be evaluated.
+ */
+ measurement?: string;
+
+ /**
+ * The operator to be used for the evaluation.
+ */
+ operator?: 'is' | '>' | '>=' | '<' | '<=' | '!=';
+
+ /**
+ * Whether to use automatic anomaly detection or manual thresholds
+ */
+ thresholdMode?: 'automatic' | 'manual';
+
+ /**
+ * The value to be compared.
+ */
+ value?: number | boolean | string | Array;
+ }
+
+ export namespace Threshold {
+ export interface InsightParameter {
+ /**
+ * The name of the insight filter.
+ */
+ name: string;
+
+ value: unknown;
+ }
+ }
+ }
+}
+
+export interface TestListParams {
+ /**
+ * Filter for archived tests.
+ */
+ includeArchived?: boolean;
+
+ /**
+ * Retrive tests created by a specific project version.
+ */
+ originVersionId?: string | null;
+
+ /**
+ * The page to return in a paginated query.
+ */
+ page?: number;
+
+ /**
+ * Maximum number of items to return per page.
+ */
+ perPage?: number;
+
+ /**
+ * Filter for suggested tests.
+ */
+ suggested?: boolean;
+
+ /**
+ * Filter objects by test type. Available types are `integrity`, `consistency`,
+ * `performance`, `fairness`, and `robustness`.
+ */
+ type?: 'integrity' | 'consistency' | 'performance' | 'fairness' | 'robustness';
+
+ /**
+ * Retrive tests with usesProductionData (monitoring).
+ */
+ usesProductionData?: boolean | null;
+}
+
+export declare namespace Tests {
+ export {
+ type TestCreateResponse as TestCreateResponse,
+ type TestUpdateResponse as TestUpdateResponse,
+ type TestListResponse as TestListResponse,
+ type TestCreateParams as TestCreateParams,
+ type TestUpdateParams as TestUpdateParams,
+ type TestListParams as TestListParams,
+ };
+}
diff --git a/src/version.ts b/src/version.ts
index ce6b899..9d013cc 100644
--- a/src/version.ts
+++ b/src/version.ts
@@ -1 +1 @@
-export const VERSION = '0.12.0'; // x-release-please-version
+export const VERSION = '0.13.0'; // x-release-please-version
diff --git a/tests/api-resources/commits/test-results.test.ts b/tests/api-resources/commits/test-results.test.ts
index 49a096a..9be61d7 100644
--- a/tests/api-resources/commits/test-results.test.ts
+++ b/tests/api-resources/commits/test-results.test.ts
@@ -34,7 +34,7 @@ describe('resource testResults', () => {
await expect(
client.commits.testResults.list(
'182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e',
- { includeArchived: true, page: 1, perPage: 1, status: 'running', type: 'integrity' },
+ { includeArchived: true, page: 1, perPage: 1, status: 'passing', type: 'integrity' },
{ path: '/_stainless_unknown_path' },
),
).rejects.toThrow(Openlayer.NotFoundError);
diff --git a/tests/api-resources/inference-pipelines/test-results.test.ts b/tests/api-resources/inference-pipelines/test-results.test.ts
index 5b55a92..0bf1ad4 100644
--- a/tests/api-resources/inference-pipelines/test-results.test.ts
+++ b/tests/api-resources/inference-pipelines/test-results.test.ts
@@ -36,7 +36,7 @@ describe('resource testResults', () => {
await expect(
client.inferencePipelines.testResults.list(
'182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e',
- { page: 1, perPage: 1, status: 'running', type: 'integrity' },
+ { page: 1, perPage: 1, status: 'passing', type: 'integrity' },
{ path: '/_stainless_unknown_path' },
),
).rejects.toThrow(Openlayer.NotFoundError);
diff --git a/tests/api-resources/projects/tests.test.ts b/tests/api-resources/projects/tests.test.ts
new file mode 100644
index 0000000..9a6c452
--- /dev/null
+++ b/tests/api-resources/projects/tests.test.ts
@@ -0,0 +1,131 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import Openlayer from 'openlayer';
+import { Response } from 'node-fetch';
+
+const client = new Openlayer({
+ apiKey: 'My API Key',
+ baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+});
+
+describe('resource tests', () => {
+ test('create: only required params', async () => {
+ const responsePromise = client.projects.tests.create('182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e', {
+ description: 'This test checks for duplicate rows in the dataset.',
+ name: 'No duplicate rows',
+ subtype: 'duplicateRowCount',
+ thresholds: [{}],
+ type: 'integrity',
+ });
+ const rawResponse = await responsePromise.asResponse();
+ expect(rawResponse).toBeInstanceOf(Response);
+ const response = await responsePromise;
+ expect(response).not.toBeInstanceOf(Response);
+ const dataAndResponse = await responsePromise.withResponse();
+ expect(dataAndResponse.data).toBe(response);
+ expect(dataAndResponse.response).toBe(rawResponse);
+ });
+
+ test('create: required and optional params', async () => {
+ const response = await client.projects.tests.create('182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e', {
+ description: 'This test checks for duplicate rows in the dataset.',
+ name: 'No duplicate rows',
+ subtype: 'duplicateRowCount',
+ thresholds: [
+ {
+ insightName: 'duplicateRowCount',
+ insightParameters: [{ name: 'column_name', value: 'Age' }],
+ measurement: 'duplicateRowCount',
+ operator: '<=',
+ thresholdMode: 'automatic',
+ value: 0,
+ },
+ ],
+ type: 'integrity',
+ archived: false,
+ delayWindow: 0,
+ evaluationWindow: 3600,
+ usesMlModel: false,
+ usesProductionData: false,
+ usesReferenceDataset: false,
+ usesTrainingDataset: false,
+ usesValidationDataset: true,
+ });
+ });
+
+ test('update: only required params', async () => {
+ const responsePromise = client.projects.tests.update('182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e', {
+ payloads: [{ id: '182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e' }],
+ });
+ const rawResponse = await responsePromise.asResponse();
+ expect(rawResponse).toBeInstanceOf(Response);
+ const response = await responsePromise;
+ expect(response).not.toBeInstanceOf(Response);
+ const dataAndResponse = await responsePromise.withResponse();
+ expect(dataAndResponse.data).toBe(response);
+ expect(dataAndResponse.response).toBe(rawResponse);
+ });
+
+ test('update: required and optional params', async () => {
+ const response = await client.projects.tests.update('182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e', {
+ payloads: [
+ {
+ id: '182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e',
+ archived: false,
+ description: 'This test checks for duplicate rows in the dataset.',
+ name: 'No duplicate rows',
+ suggested: false,
+ thresholds: [
+ {
+ insightName: 'duplicateRowCount',
+ insightParameters: [{ name: 'column_name', value: 'Age' }],
+ measurement: 'duplicateRowCount',
+ operator: '<=',
+ thresholdMode: 'automatic',
+ value: 0,
+ },
+ ],
+ },
+ ],
+ });
+ });
+
+ test('list', async () => {
+ const responsePromise = client.projects.tests.list('182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e');
+ const rawResponse = await responsePromise.asResponse();
+ expect(rawResponse).toBeInstanceOf(Response);
+ const response = await responsePromise;
+ expect(response).not.toBeInstanceOf(Response);
+ const dataAndResponse = await responsePromise.withResponse();
+ expect(dataAndResponse.data).toBe(response);
+ expect(dataAndResponse.response).toBe(rawResponse);
+ });
+
+ test('list: request options instead of params are passed correctly', async () => {
+ // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error
+ await expect(
+ client.projects.tests.list('182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e', {
+ path: '/_stainless_unknown_path',
+ }),
+ ).rejects.toThrow(Openlayer.NotFoundError);
+ });
+
+ test('list: request options and params are passed correctly', async () => {
+ // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error
+ await expect(
+ client.projects.tests.list(
+ '182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e',
+ {
+ includeArchived: true,
+ originVersionId: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
+ page: 1,
+ perPage: 1,
+ suggested: true,
+ type: 'integrity',
+ usesProductionData: true,
+ },
+ { path: '/_stainless_unknown_path' },
+ ),
+ ).rejects.toThrow(Openlayer.NotFoundError);
+ });
+});
diff --git a/tests/index.test.ts b/tests/index.test.ts
index f45c1b0..1b7af15 100644
--- a/tests/index.test.ts
+++ b/tests/index.test.ts
@@ -96,6 +96,15 @@ describe('instantiate client', () => {
expect(response).toEqual({ url: 'http://localhost:5000/foo', custom: true });
});
+ test('explicit global fetch', async () => {
+ // make sure the global fetch type is assignable to our Fetch type
+ const client = new Openlayer({
+ baseURL: 'http://localhost:5000/',
+ apiKey: 'My API Key',
+ fetch: defaultFetch,
+ });
+ });
+
test('custom signal', async () => {
const client = new Openlayer({
baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
@@ -176,6 +185,28 @@ describe('instantiate client', () => {
const client = new Openlayer({ apiKey: 'My API Key' });
expect(client.baseURL).toEqual('https://api.openlayer.com/v1');
});
+
+ test('in request options', () => {
+ const client = new Openlayer({ apiKey: 'My API Key' });
+ expect(client.buildURL('/foo', null, 'http://localhost:5000/option')).toEqual(
+ 'http://localhost:5000/option/foo',
+ );
+ });
+
+ test('in request options overridden by client options', () => {
+ const client = new Openlayer({ apiKey: 'My API Key', baseURL: 'http://localhost:5000/client' });
+ expect(client.buildURL('/foo', null, 'http://localhost:5000/option')).toEqual(
+ 'http://localhost:5000/client/foo',
+ );
+ });
+
+ test('in request options overridden by env variable', () => {
+ process.env['OPENLAYER_BASE_URL'] = 'http://localhost:5000/env';
+ const client = new Openlayer({ apiKey: 'My API Key' });
+ expect(client.buildURL('/foo', null, 'http://localhost:5000/option')).toEqual(
+ 'http://localhost:5000/env/foo',
+ );
+ });
});
test('maxRetries option is correctly set', () => {