From 2c3132424b3a0eeddc35b1dce3cff324b8453022 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Mon, 22 Jul 2024 11:10:38 +0000
Subject: [PATCH] feat(api): add uploads endpoints (#946)
---
.stats.yml | 4 +-
api.md | 22 +++
src/index.ts | 6 +
src/resources/chat/completions.ts | 1 +
src/resources/index.ts | 1 +
src/resources/uploads/index.ts | 4 +
src/resources/uploads/parts.ts | 68 ++++++++
src/resources/uploads/uploads.ts | 169 ++++++++++++++++++++
tests/api-resources/uploads/parts.test.ts | 30 ++++
tests/api-resources/uploads/uploads.test.ts | 74 +++++++++
10 files changed, 377 insertions(+), 2 deletions(-)
create mode 100644 src/resources/uploads/index.ts
create mode 100644 src/resources/uploads/parts.ts
create mode 100644 src/resources/uploads/uploads.ts
create mode 100644 tests/api-resources/uploads/parts.test.ts
create mode 100644 tests/api-resources/uploads/uploads.test.ts
diff --git a/.stats.yml b/.stats.yml
index 27e2ce5ed..4e4cb5509 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,2 +1,2 @@
-configured_endpoints: 64
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-518ca6c60061d3e8bc0971facf40d752f2aea62e3522cc168ad29a1f29cab3dd.yml
+configured_endpoints: 68
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-77cfff37114bc9f141c7e6107eb5f1b38d8cc99bc3d4ce03a066db2b6b649c69.yml
diff --git a/api.md b/api.md
index 423a067c8..044d8ff25 100644
--- a/api.md
+++ b/api.md
@@ -362,3 +362,25 @@ Methods:
- client.batches.retrieve(batchId) -> Batch
- client.batches.list({ ...params }) -> BatchesPage
- client.batches.cancel(batchId) -> Batch
+
+# Uploads
+
+Types:
+
+- Upload
+
+Methods:
+
+- client.uploads.create({ ...params }) -> Upload
+- client.uploads.cancel(uploadId) -> Upload
+- client.uploads.complete(uploadId, { ...params }) -> Upload
+
+## Parts
+
+Types:
+
+- UploadPart
+
+Methods:
+
+- client.uploads.parts.create(uploadId, { ...params }) -> UploadPart
diff --git a/src/index.ts b/src/index.ts
index df4ca86d7..0c10128d5 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -164,6 +164,7 @@ export class OpenAI extends Core.APIClient {
fineTuning: API.FineTuning = new API.FineTuning(this);
beta: API.Beta = new API.Beta(this);
batches: API.Batches = new API.Batches(this);
+ uploads: API.Uploads = new API.Uploads(this);
protected override defaultQuery(): Core.DefaultQuery | undefined {
return this._options.defaultQuery;
@@ -309,6 +310,11 @@ export namespace OpenAI {
export import BatchCreateParams = API.BatchCreateParams;
export import BatchListParams = API.BatchListParams;
+ export import Uploads = API.Uploads;
+ export import Upload = API.Upload;
+ export import UploadCreateParams = API.UploadCreateParams;
+ export import UploadCompleteParams = API.UploadCompleteParams;
+
export import ErrorObject = API.ErrorObject;
export import FunctionDefinition = API.FunctionDefinition;
export import FunctionParameters = API.FunctionParameters;
diff --git a/src/resources/chat/completions.ts b/src/resources/chat/completions.ts
index 44eb9520c..4027e995b 100644
--- a/src/resources/chat/completions.ts
+++ b/src/resources/chat/completions.ts
@@ -820,6 +820,7 @@ export interface ChatCompletionCreateParamsBase {
* exhausted.
* - If set to 'default', the request will be processed using the default service
* tier with a lower uptime SLA and no latency guarentee.
+ * - When not set, the default behavior is 'auto'.
*
* When this parameter is set, the response body will include the `service_tier`
* utilized.
diff --git a/src/resources/index.ts b/src/resources/index.ts
index 6f8e8564c..9f2a3cbe7 100644
--- a/src/resources/index.ts
+++ b/src/resources/index.ts
@@ -43,3 +43,4 @@ export {
} from './images';
export { Model, ModelDeleted, ModelsPage, Models } from './models';
export { Moderation, ModerationCreateResponse, ModerationCreateParams, Moderations } from './moderations';
+export { Upload, UploadCreateParams, UploadCompleteParams, Uploads } from './uploads/uploads';
diff --git a/src/resources/uploads/index.ts b/src/resources/uploads/index.ts
new file mode 100644
index 000000000..1a353d312
--- /dev/null
+++ b/src/resources/uploads/index.ts
@@ -0,0 +1,4 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+export { Upload, UploadCreateParams, UploadCompleteParams, Uploads } from './uploads';
+export { UploadPart, PartCreateParams, Parts } from './parts';
diff --git a/src/resources/uploads/parts.ts b/src/resources/uploads/parts.ts
new file mode 100644
index 000000000..a4af5c606
--- /dev/null
+++ b/src/resources/uploads/parts.ts
@@ -0,0 +1,68 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIResource } from '../../resource';
+import * as Core from '../../core';
+import * as PartsAPI from './parts';
+
+export class Parts extends APIResource {
+ /**
+ * Adds a
+ * [Part](https://platform.openai.com/docs/api-reference/uploads/part-object) to an
+ * [Upload](https://platform.openai.com/docs/api-reference/uploads/object) object.
+ * A Part represents a chunk of bytes from the file you are trying to upload.
+ *
+ * Each Part can be at most 64 MB, and you can add Parts until you hit the Upload
+ * maximum of 8 GB.
+ *
+ * It is possible to add multiple Parts in parallel. You can decide the intended
+ * order of the Parts when you
+ * [complete the Upload](https://platform.openai.com/docs/api-reference/uploads/complete).
+ */
+ create(
+ uploadId: string,
+ body: PartCreateParams,
+ options?: Core.RequestOptions,
+ ): Core.APIPromise {
+ return this._client.post(
+ `/uploads/${uploadId}/parts`,
+ Core.multipartFormRequestOptions({ body, ...options }),
+ );
+ }
+}
+
+/**
+ * The upload Part represents a chunk of bytes we can add to an Upload object.
+ */
+export interface UploadPart {
+ /**
+ * The upload Part unique identifier, which can be referenced in API endpoints.
+ */
+ id: string;
+
+ /**
+ * The Unix timestamp (in seconds) for when the Part was created.
+ */
+ created_at: number;
+
+ /**
+ * The object type, which is always `upload.part`.
+ */
+ object: 'upload.part';
+
+ /**
+ * The ID of the Upload object that this Part was added to.
+ */
+ upload_id: string;
+}
+
+export interface PartCreateParams {
+ /**
+ * The chunk of bytes for this Part.
+ */
+ data: Core.Uploadable;
+}
+
+export namespace Parts {
+ export import UploadPart = PartsAPI.UploadPart;
+ export import PartCreateParams = PartsAPI.PartCreateParams;
+}
diff --git a/src/resources/uploads/uploads.ts b/src/resources/uploads/uploads.ts
new file mode 100644
index 000000000..ceb2b6d23
--- /dev/null
+++ b/src/resources/uploads/uploads.ts
@@ -0,0 +1,169 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIResource } from '../../resource';
+import * as Core from '../../core';
+import * as UploadsAPI from './uploads';
+import * as FilesAPI from '../files';
+import * as PartsAPI from './parts';
+
+export class Uploads extends APIResource {
+ parts: PartsAPI.Parts = new PartsAPI.Parts(this._client);
+
+ /**
+ * Creates an intermediate
+ * [Upload](https://platform.openai.com/docs/api-reference/uploads/object) object
+ * that you can add
+ * [Parts](https://platform.openai.com/docs/api-reference/uploads/part-object) to.
+ * Currently, an Upload can accept at most 8 GB in total and expires after an hour
+ * after you create it.
+ *
+ * Once you complete the Upload, we will create a
+ * [File](https://platform.openai.com/docs/api-reference/files/object) object that
+ * contains all the parts you uploaded. This File is usable in the rest of our
+ * platform as a regular File object.
+ *
+ * For certain `purpose`s, the correct `mime_type` must be specified. Please refer
+ * to documentation for the supported MIME types for your use case:
+ *
+ * - [Assistants](https://platform.openai.com/docs/assistants/tools/file-search/supported-files)
+ *
+ * For guidance on the proper filename extensions for each purpose, please follow
+ * the documentation on
+ * [creating a File](https://platform.openai.com/docs/api-reference/files/create).
+ */
+ create(body: UploadCreateParams, options?: Core.RequestOptions): Core.APIPromise {
+ return this._client.post('/uploads', { body, ...options });
+ }
+
+ /**
+ * Cancels the Upload. No Parts may be added after an Upload is cancelled.
+ */
+ cancel(uploadId: string, options?: Core.RequestOptions): Core.APIPromise {
+ return this._client.post(`/uploads/${uploadId}/cancel`, options);
+ }
+
+ /**
+ * Completes the
+ * [Upload](https://platform.openai.com/docs/api-reference/uploads/object).
+ *
+ * Within the returned Upload object, there is a nested
+ * [File](https://platform.openai.com/docs/api-reference/files/object) object that
+ * is ready to use in the rest of the platform.
+ *
+ * You can specify the order of the Parts by passing in an ordered list of the Part
+ * IDs.
+ *
+ * The number of bytes uploaded upon completion must match the number of bytes
+ * initially specified when creating the Upload object. No Parts may be added after
+ * an Upload is completed.
+ */
+ complete(
+ uploadId: string,
+ body: UploadCompleteParams,
+ options?: Core.RequestOptions,
+ ): Core.APIPromise {
+ return this._client.post(`/uploads/${uploadId}/complete`, { body, ...options });
+ }
+}
+
+/**
+ * The Upload object can accept byte chunks in the form of Parts.
+ */
+export interface Upload {
+ /**
+ * The Upload unique identifier, which can be referenced in API endpoints.
+ */
+ id: string;
+
+ /**
+ * The intended number of bytes to be uploaded.
+ */
+ bytes: number;
+
+ /**
+ * The Unix timestamp (in seconds) for when the Upload was created.
+ */
+ created_at: number;
+
+ /**
+ * The Unix timestamp (in seconds) for when the Upload was created.
+ */
+ expires_at: number;
+
+ /**
+ * The name of the file to be uploaded.
+ */
+ filename: string;
+
+ /**
+ * The object type, which is always "upload".
+ */
+ object: 'upload';
+
+ /**
+ * The intended purpose of the file.
+ * [Please refer here](https://platform.openai.com/docs/api-reference/files/object#files/object-purpose)
+ * for acceptable values.
+ */
+ purpose: string;
+
+ /**
+ * The status of the Upload.
+ */
+ status: 'pending' | 'completed' | 'cancelled' | 'expired';
+
+ /**
+ * The ready File object after the Upload is completed.
+ */
+ file?: FilesAPI.FileObject | null;
+}
+
+export interface UploadCreateParams {
+ /**
+ * The number of bytes in the file you are uploading.
+ */
+ bytes: number;
+
+ /**
+ * The name of the file to upload.
+ */
+ filename: string;
+
+ /**
+ * The MIME type of the file.
+ *
+ * This must fall within the supported MIME types for your file purpose. See the
+ * supported MIME types for assistants and vision.
+ */
+ mime_type: string;
+
+ /**
+ * The intended purpose of the uploaded file.
+ *
+ * See the
+ * [documentation on File purposes](https://platform.openai.com/docs/api-reference/files/create#files-create-purpose).
+ */
+ purpose: 'assistants' | 'batch' | 'fine-tune' | 'vision';
+}
+
+export interface UploadCompleteParams {
+ /**
+ * The ordered list of Part IDs.
+ */
+ part_ids: Array;
+
+ /**
+ * The optional md5 checksum for the file contents to verify if the bytes uploaded
+ * matches what you expect.
+ */
+ md5?: string;
+}
+
+export namespace Uploads {
+ export import Upload = UploadsAPI.Upload;
+ export import UploadCreateParams = UploadsAPI.UploadCreateParams;
+ export import UploadCompleteParams = UploadsAPI.UploadCompleteParams;
+ export import Parts = PartsAPI.Parts;
+ export import UploadPart = PartsAPI.UploadPart;
+ export import PartCreateParams = PartsAPI.PartCreateParams;
+}
diff --git a/tests/api-resources/uploads/parts.test.ts b/tests/api-resources/uploads/parts.test.ts
new file mode 100644
index 000000000..5e69c5861
--- /dev/null
+++ b/tests/api-resources/uploads/parts.test.ts
@@ -0,0 +1,30 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import OpenAI, { toFile } from 'openai';
+import { Response } from 'node-fetch';
+
+const openai = new OpenAI({
+ apiKey: 'My API Key',
+ baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+});
+
+describe('resource parts', () => {
+ test('create: only required params', async () => {
+ const responsePromise = openai.uploads.parts.create('upload_abc123', {
+ data: await toFile(Buffer.from('# my file contents'), 'README.md'),
+ });
+ 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 openai.uploads.parts.create('upload_abc123', {
+ data: await toFile(Buffer.from('# my file contents'), 'README.md'),
+ });
+ });
+});
diff --git a/tests/api-resources/uploads/uploads.test.ts b/tests/api-resources/uploads/uploads.test.ts
new file mode 100644
index 000000000..08f059d1b
--- /dev/null
+++ b/tests/api-resources/uploads/uploads.test.ts
@@ -0,0 +1,74 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import OpenAI from 'openai';
+import { Response } from 'node-fetch';
+
+const openai = new OpenAI({
+ apiKey: 'My API Key',
+ baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+});
+
+describe('resource uploads', () => {
+ test('create: only required params', async () => {
+ const responsePromise = openai.uploads.create({
+ bytes: 0,
+ filename: 'filename',
+ mime_type: 'mime_type',
+ purpose: 'assistants',
+ });
+ 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 openai.uploads.create({
+ bytes: 0,
+ filename: 'filename',
+ mime_type: 'mime_type',
+ purpose: 'assistants',
+ });
+ });
+
+ test('cancel', async () => {
+ const responsePromise = openai.uploads.cancel('upload_abc123');
+ 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('cancel: 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(
+ openai.uploads.cancel('upload_abc123', { path: '/_stainless_unknown_path' }),
+ ).rejects.toThrow(OpenAI.NotFoundError);
+ });
+
+ test('complete: only required params', async () => {
+ const responsePromise = openai.uploads.complete('upload_abc123', {
+ part_ids: ['string', 'string', 'string'],
+ });
+ 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('complete: required and optional params', async () => {
+ const response = await openai.uploads.complete('upload_abc123', {
+ part_ids: ['string', 'string', 'string'],
+ md5: 'md5',
+ });
+ });
+});