Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(client): add auto-pagination to fine tuning list endpoints #254

Merged
merged 1 commit into from
Aug 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,37 @@ On timeout, an `APIConnectionTimeoutError` is thrown.

Note that requests which time out will be [retried twice by default](#retries).

## Auto-pagination

List methods in the OpenAI API are paginated.
You can use `for await … of` syntax to iterate through items across all pages:

```ts
async function fetchAllFineTuningJobs(params) {
const allFineTuningJobs = [];
// Automatically fetches more pages as needed.
for await (const job of openai.fineTuning.jobs.list({ limit: 20 })) {
allFineTuningJobs.push(job);
}
return allFineTuningJobs;
}
```

Alternatively, you can make request a single page at a time:

```ts
let page = await openai.fineTuning.jobs.list({ limit: 20 });
for (const job of page.data) {
console.log(job);
}

// Convenience methods are provided for manually paginating:
while (page.hasNextPage()) {
page = page.getNextPage();
// ...
}
```

## Advanced Usage

### Accessing raw Response data (e.g., headers)
Expand Down
4 changes: 4 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,10 @@ export namespace OpenAI {
export import Page = Pagination.Page;
export import PageResponse = Pagination.PageResponse;

export import CursorPage = Pagination.CursorPage;
export import CursorPageParams = Pagination.CursorPageParams;
export import CursorPageResponse = Pagination.CursorPageResponse;

export import Completions = API.Completions;
export import Completion = API.Completion;
export import CompletionChoice = API.CompletionChoice;
Expand Down
60 changes: 59 additions & 1 deletion src/pagination.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// File generated from our OpenAPI spec by Stainless.

import { AbstractPage, Response, APIClient, FinalRequestOptions } from './core';
import { AbstractPage, Response, APIClient, FinalRequestOptions, PageInfo } from './core';

export interface PageResponse<Item> {
data: Array<Item>;
Expand Down Expand Up @@ -40,3 +40,61 @@ export class Page<Item> extends AbstractPage<Item> implements PageResponse<Item>
return null;
}
}

export interface CursorPageResponse<Item> {
data: Array<Item>;
}

export interface CursorPageParams {
/**
* Identifier for the last job from the previous pagination request.
*/
after?: string;

/**
* Number of fine-tuning jobs to retrieve.
*/
limit?: number;
}

export class CursorPage<Item extends { id: string }>
extends AbstractPage<Item>
implements CursorPageResponse<Item>
{
data: Array<Item>;

constructor(
client: APIClient,
response: Response,
body: CursorPageResponse<Item>,
options: FinalRequestOptions,
) {
super(client, response, body, options);

this.data = body.data;
}

getPaginatedItems(): Item[] {
return this.data;
}

// @deprecated Please use `nextPageInfo()` instead
nextPageParams(): Partial<CursorPageParams> | null {
const info = this.nextPageInfo();
if (!info) return null;
if ('params' in info) return info.params;
const params = Object.fromEntries(info.url.searchParams);
if (!Object.keys(params).length) return null;
return params;
}

nextPageInfo(): PageInfo | null {
if (!this.data?.length) {
return null;
}

const next = this.data[this.data.length - 1]?.id;
if (!next) return null;
return { params: { after: next } };
}
}
36 changes: 5 additions & 31 deletions src/resources/fine-tuning/jobs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { APIResource } from 'openai/resource';
import { isRequestOptions } from 'openai/core';
import * as Files from 'openai/resources/files';
import * as API from './index';
import { Page } from 'openai/pagination';
import { CursorPage, CursorPageParams } from 'openai/pagination';

export class Jobs extends APIResource {
/**
Expand Down Expand Up @@ -81,17 +81,11 @@ export class Jobs extends APIResource {
}
}

/**
* Note: no pagination actually occurs yet, this is for forwards-compatibility.
*/
export class FineTuningJobsPage extends Page<FineTuningJob> {}
export class FineTuningJobsPage extends CursorPage<FineTuningJob> {}
// alias so we can export it in the namespace
type _FineTuningJobsPage = FineTuningJobsPage;

/**
* Note: no pagination actually occurs yet, this is for forwards-compatibility.
*/
export class FineTuningJobEventsPage extends Page<FineTuningJobEvent> {}
export class FineTuningJobEventsPage extends CursorPage<FineTuningJobEvent> {}
// alias so we can export it in the namespace
type _FineTuningJobEventsPage = FineTuningJobEventsPage;

Expand Down Expand Up @@ -258,29 +252,9 @@ export namespace JobCreateParams {
}
}

export interface JobListParams {
/**
* Identifier for the last job from the previous pagination request.
*/
after?: string;

/**
* Number of fine-tuning jobs to retrieve.
*/
limit?: number;
}

export interface JobListEventsParams {
/**
* Identifier for the last event from the previous pagination request.
*/
after?: string;
export interface JobListParams extends CursorPageParams {}

/**
* Number of events to retrieve.
*/
limit?: number;
}
export interface JobListEventsParams extends CursorPageParams {}

export namespace Jobs {
export import FineTuningJob = API.FineTuningJob;
Expand Down