openai 0.0.8 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,7 @@
1
+ [![Build Status](https://github.com/ceifa/openai/actions/workflows/publish.yml/badge.svg)](https://github.com/ceifa/openai/actions/workflows/publish.yml)
2
+ [![npm](https://img.shields.io/npm/v/openai.svg)](https://npmjs.com/package/openai)
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
+
1
5
  # OpenAI
2
6
 
3
7
  A tiny async production-ready wrapper for [OpenAI GPT-3 API](https://beta.openai.com/docs/api-reference/introduction).
@@ -58,12 +62,18 @@ const completion = await openai.complete('curie', {
58
62
 
59
63
  The options argument(2nd) properties follow the exactly same names as shown on official docs.
60
64
 
65
+ Make a completion from a fine-tuned model:
66
+
67
+ ```js
68
+ const completion = await openai.completeFromModel('FINE_TUNED_MODEL', {
69
+ prompt: 'Q: Hello\nA:'
70
+ });
71
+ ```
72
+
61
73
  Make a completion and stream the response:
62
74
 
63
75
  ```js
64
- // Very experimental! Don't use on production!!!
65
- // This API may change at any time
66
- const stream = await openai.completionTextStream('curie', {
76
+ const stream = await openai.completeAndStream('curie', { // or completeFromModelAndStream
67
77
  prompt: 'Q: Hello\nA:',
68
78
  user: 'user-123'
69
79
  });
@@ -191,4 +201,4 @@ Get fine-tune events of a fine-tune:
191
201
 
192
202
  ```js
193
203
  const events = await openai.getFinetuneEvents('ftjob-AF1WoRqd3aJ');
194
- ```
204
+ ```
package/dist/index.d.ts CHANGED
@@ -1,27 +1,2 @@
1
- /// <reference types="node" />
2
- import { Answer, AnswerRequest, Classification, ClassificationRequest, Completion, CompletionRequest, ContentLabel, Engine, EngineId, File, FilePurpose, FineTune, FineTuneEvent, FineTuneRequest, JsonLines, SearchDocument, SearchRequest } from './types';
3
- import { Readable } from 'stream';
4
- export declare class OpenAI {
5
- private readonly url;
6
- private readonly headers;
7
- constructor(apiKey: string, organizationId?: string, version?: string);
8
- getEngines(): Promise<Engine[]>;
9
- getEngine(engine: EngineId): Promise<Engine>;
10
- complete(engine: EngineId, options: CompletionRequest): Promise<Completion>;
11
- completionTextStream(engine: EngineId, options: CompletionRequest): Promise<Readable>;
12
- contentFilter(content: string, user?: string): Promise<ContentLabel>;
13
- search(engine: EngineId, options: SearchRequest): Promise<SearchDocument[]>;
14
- classify(options: ClassificationRequest): Promise<Classification>;
15
- answer(options: AnswerRequest): Promise<Answer>;
16
- getFiles(): Promise<File[]>;
17
- uploadFile(file: string, jsonlines: JsonLines, purpose: FilePurpose): Promise<File>;
18
- getFile(fileId: string): Promise<File>;
19
- deleteFile(fileId: string): Promise<void>;
20
- finetune(options: FineTuneRequest): Promise<FineTune>;
21
- getFinetunes(): Promise<FineTune[]>;
22
- getFinetune(finetuneId: string): Promise<FineTune>;
23
- cancelFinetune(finetuneId: string): Promise<FineTune>;
24
- getFinetuneEvents(finetuneId: string): Promise<FineTuneEvent[]>;
25
- private requestRaw;
26
- private request;
27
- }
1
+ export * from './lib.js';
2
+ export * from './types.js';
package/dist/index.js CHANGED
@@ -1,173 +1,2 @@
1
- (function (factory) {
2
- if (typeof module === "object" && typeof module.exports === "object") {
3
- var v = factory(require, exports);
4
- if (v !== undefined) module.exports = v;
5
- }
6
- else if (typeof define === "function" && define.amd) {
7
- define(["require", "exports", "tslib", "stream", "form-data", "node-fetch"], factory);
8
- }
9
- })(function (require, exports) {
10
- "use strict";
11
- Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.OpenAI = void 0;
13
- const tslib_1 = require("tslib");
14
- const stream_1 = require("stream");
15
- const form_data_1 = tslib_1.__importDefault(require("form-data"));
16
- const node_fetch_1 = tslib_1.__importDefault(require("node-fetch"));
17
- const baseUrl = 'https://api.openai.com';
18
- const defaultVersion = 'v1';
19
- class OpenAI {
20
- url;
21
- headers;
22
- constructor(apiKey, organizationId, version = defaultVersion) {
23
- this.headers = {
24
- 'authorization': `Bearer ${apiKey}`,
25
- 'content-type': 'application/json',
26
- };
27
- if (organizationId) {
28
- this.headers['openai-organization'] = organizationId;
29
- }
30
- this.url = `${baseUrl}/${version}`;
31
- }
32
- getEngines() {
33
- return this.request('/engines', 'GET').then((r) => r.data);
34
- }
35
- getEngine(engine) {
36
- return this.request(`/engines/${engine}`, 'GET');
37
- }
38
- complete(engine, options) {
39
- return this.request(`/engines/${engine}/completions`, 'POST', options);
40
- }
41
- async completionTextStream(engine, options) {
42
- const request = await this.requestRaw(`/engines/${engine}/completions`, 'POST', { ...options, stream: true });
43
- console.warn("Stream completion is an experimental feature, don't use on production");
44
- const transform = new stream_1.Transform({
45
- transform: (chunk, _, callback) => {
46
- const body = chunk.slice(6).toString().trim();
47
- if (body && body[0] !== '[') {
48
- try {
49
- const completion = JSON.parse(body);
50
- return callback(undefined, completion.choices[0].text);
51
- }
52
- catch (e) {
53
- throw new Error(`Faile to parse: "${chunk.toString()}"`);
54
- }
55
- }
56
- callback();
57
- },
58
- });
59
- return request.body.pipe(transform);
60
- }
61
- async contentFilter(content, user) {
62
- const completion = await this.complete('content-filter-alpha-c4', {
63
- prompt: `<|endoftext|>${content}\n--\nLabel:`,
64
- temperature: 0,
65
- max_tokens: 1,
66
- top_p: 1,
67
- frequency_penalty: 0,
68
- presence_penalty: 0,
69
- logprobs: 10,
70
- user,
71
- });
72
- let label = Number(completion.choices[0].text);
73
- if (label === 2) {
74
- const logprobs = completion.choices[0].logprobs.top_logprobs[0];
75
- if (logprobs['2'] < -0.355) {
76
- if (logprobs['0'] && logprobs['1']) {
77
- label = logprobs['0'] >= logprobs['1'] ? 0 : 1;
78
- }
79
- else if (logprobs['0']) {
80
- label = 0;
81
- }
82
- else if (logprobs['1']) {
83
- label = 1;
84
- }
85
- }
86
- }
87
- if (![0, 1, 2].includes(label)) {
88
- label = 2;
89
- }
90
- return label;
91
- }
92
- search(engine, options) {
93
- return this.request(`/engines/${engine}/search`, 'POST', options).then((r) => r.data);
94
- }
95
- classify(options) {
96
- return this.request('/classifications', 'POST', options);
97
- }
98
- answer(options) {
99
- return this.request('/answers', 'POST', options);
100
- }
101
- getFiles() {
102
- return this.request('/files', 'GET').then((r) => r.data);
103
- }
104
- uploadFile(file, jsonlines, purpose) {
105
- const data = new form_data_1.default();
106
- let fileJsonlines;
107
- if (Array.isArray(jsonlines)) {
108
- if (typeof jsonlines[0] === 'object') {
109
- jsonlines = jsonlines.map((j) => JSON.stringify(j));
110
- }
111
- fileJsonlines = jsonlines.join('\n');
112
- }
113
- else {
114
- fileJsonlines = jsonlines;
115
- }
116
- data.append('file', fileJsonlines, file);
117
- data.append('purpose', purpose);
118
- return this.request('/files', 'POST', data);
119
- }
120
- getFile(fileId) {
121
- return this.request(`/files/${fileId}`, 'GET');
122
- }
123
- deleteFile(fileId) {
124
- return this.request(`/files/${fileId}`, 'DELETE');
125
- }
126
- finetune(options) {
127
- return this.request(`/fine-tunes`, 'POST', options);
128
- }
129
- getFinetunes() {
130
- return this.request('/fine-tunes', 'GET').then((r) => r.data);
131
- }
132
- getFinetune(finetuneId) {
133
- return this.request(`/fine-tunes/${finetuneId}`, 'GET');
134
- }
135
- cancelFinetune(finetuneId) {
136
- return this.request(`/fine-tunes/${finetuneId}/cancel`, 'POST');
137
- }
138
- getFinetuneEvents(finetuneId) {
139
- return this.request(`/fine-tunes/${finetuneId}/events`, 'GET').then((r) => r.data);
140
- }
141
- async requestRaw(path, method, body) {
142
- let headers = { ...this.headers };
143
- if (body instanceof form_data_1.default) {
144
- delete headers['content-type'];
145
- headers = body.getHeaders(headers);
146
- }
147
- else if (!['string', 'undefined'].includes(typeof body)) {
148
- body = JSON.stringify(body);
149
- }
150
- const response = await node_fetch_1.default(this.url + path, {
151
- headers,
152
- method,
153
- body: body,
154
- });
155
- if (!response.ok) {
156
- let errorBody;
157
- try {
158
- errorBody = await response.text();
159
- }
160
- catch {
161
- errorBody = 'Failed to get body as text';
162
- }
163
- throw new Error(`OpenAI did not return ok: ${response.status} ~ Error body: ${errorBody}`);
164
- }
165
- return response;
166
- }
167
- async request(path, method, body) {
168
- const response = await this.requestRaw(path, method, body);
169
- return response.json();
170
- }
171
- }
172
- exports.OpenAI = OpenAI;
173
- });
1
+ export * from './lib.js';
2
+ export * from './types.js';
package/dist/lib.d.ts ADDED
@@ -0,0 +1,31 @@
1
+ /// <reference types="node" />
2
+ import { Answer, AnswerRequest, Classification, ClassificationRequest, Completion, CompletionRequest, ContentLabel, Embedding, EmbeddingRequest, Engine, EngineId, File, FilePurpose, FineTune, FineTuneEvent, FineTuneRequest, JsonLines, List, SearchDocument, SearchRequest } from './types.js';
3
+ import { Readable } from 'stream';
4
+ export declare class OpenAI {
5
+ private readonly url;
6
+ private readonly headers;
7
+ constructor(apiKey: string, organizationId?: string, version?: string);
8
+ getEngines(): Promise<Engine[]>;
9
+ getEngine(engine: EngineId): Promise<Engine>;
10
+ complete(engine: EngineId, options: CompletionRequest): Promise<Completion>;
11
+ completeFromModel(fineTunedModel: string, options: CompletionRequest): Promise<Completion>;
12
+ completeAndStream(engine: EngineId, options: CompletionRequest): Promise<Readable>;
13
+ completeFromModelAndStream(fineTunedModel: string, options: CompletionRequest): Promise<Readable>;
14
+ contentFilter(content: string, user?: string): Promise<ContentLabel>;
15
+ search(engine: EngineId, options: SearchRequest): Promise<SearchDocument[]>;
16
+ classify(options: ClassificationRequest): Promise<Classification>;
17
+ answer(options: AnswerRequest): Promise<Answer>;
18
+ getFiles(): Promise<File[]>;
19
+ uploadFile(file: string, jsonlines: JsonLines, purpose: FilePurpose): Promise<File>;
20
+ getFile(fileId: string): Promise<File>;
21
+ deleteFile(fileId: string): Promise<void>;
22
+ finetune(options: FineTuneRequest): Promise<FineTune>;
23
+ getFinetunes(): Promise<FineTune[]>;
24
+ getFinetune(finetuneId: string): Promise<FineTune>;
25
+ cancelFinetune(finetuneId: string): Promise<FineTune>;
26
+ getFinetuneEvents(finetuneId: string): Promise<FineTuneEvent[]>;
27
+ createEmbedding(engine: EngineId, options: EmbeddingRequest): Promise<List<Embedding>>;
28
+ private requestRaw;
29
+ private request;
30
+ private eventStreamTransformer;
31
+ }
package/dist/lib.js ADDED
@@ -0,0 +1,179 @@
1
+ import { ContentLabel, } from './types.js';
2
+ import { Transform } from 'stream';
3
+ import FormData from 'form-data';
4
+ import fetch from 'node-fetch';
5
+ const baseUrl = 'https://api.openai.com';
6
+ const defaultVersion = 'v1';
7
+ export class OpenAI {
8
+ constructor(apiKey, organizationId, version = defaultVersion) {
9
+ this.headers = {
10
+ 'authorization': `Bearer ${apiKey}`,
11
+ 'content-type': 'application/json',
12
+ };
13
+ if (organizationId) {
14
+ this.headers['openai-organization'] = organizationId;
15
+ }
16
+ this.url = `${baseUrl}/${version}`;
17
+ }
18
+ getEngines() {
19
+ return this.request('/engines', 'GET').then((r) => r.data);
20
+ }
21
+ getEngine(engine) {
22
+ return this.request(`/engines/${engine}`, 'GET');
23
+ }
24
+ complete(engine, options) {
25
+ return this.request(`/engines/${engine}/completions`, 'POST', options);
26
+ }
27
+ completeFromModel(fineTunedModel, options) {
28
+ return this.request(`/completions`, 'POST', { ...options, model: fineTunedModel });
29
+ }
30
+ async completeAndStream(engine, options) {
31
+ const request = await this.requestRaw(`/engines/${engine}/completions`, 'POST', { ...options, stream: true });
32
+ return request.body.pipe(this.eventStreamTransformer());
33
+ }
34
+ async completeFromModelAndStream(fineTunedModel, options) {
35
+ const request = await this.requestRaw(`/completions`, 'POST', {
36
+ ...options,
37
+ model: fineTunedModel,
38
+ stream: true,
39
+ });
40
+ return request.body.pipe(this.eventStreamTransformer());
41
+ }
42
+ async contentFilter(content, user) {
43
+ const completion = await this.complete('content-filter-alpha-c4', {
44
+ prompt: `<|endoftext|>${content}\n--\nLabel:`,
45
+ temperature: 0,
46
+ max_tokens: 1,
47
+ top_p: 1,
48
+ frequency_penalty: 0,
49
+ presence_penalty: 0,
50
+ logprobs: 10,
51
+ user,
52
+ });
53
+ let label = Number(completion.choices[0].text);
54
+ if (label === ContentLabel.Unsafe) {
55
+ const logprobs = completion.choices[0].logprobs?.top_logprobs[0];
56
+ if (logprobs && logprobs['2'] < -0.355) {
57
+ if (logprobs['0'] && logprobs['1']) {
58
+ label = logprobs['0'] >= logprobs['1'] ? ContentLabel.Safe : ContentLabel.Sensitive;
59
+ }
60
+ else if (logprobs['0']) {
61
+ label = ContentLabel.Safe;
62
+ }
63
+ else if (logprobs['1']) {
64
+ label = ContentLabel.Sensitive;
65
+ }
66
+ }
67
+ }
68
+ if (![0, 1, 2].includes(label)) {
69
+ label = ContentLabel.Unsafe;
70
+ }
71
+ return label;
72
+ }
73
+ search(engine, options) {
74
+ return this.request(`/engines/${engine}/search`, 'POST', options).then((r) => r.data);
75
+ }
76
+ classify(options) {
77
+ return this.request('/classifications', 'POST', options);
78
+ }
79
+ answer(options) {
80
+ return this.request('/answers', 'POST', options);
81
+ }
82
+ getFiles() {
83
+ return this.request('/files', 'GET').then((r) => r.data);
84
+ }
85
+ uploadFile(file, jsonlines, purpose) {
86
+ const data = new FormData();
87
+ let fileJsonlines;
88
+ if (Array.isArray(jsonlines)) {
89
+ if (typeof jsonlines[0] === 'object') {
90
+ jsonlines = jsonlines.map((j) => JSON.stringify(j));
91
+ }
92
+ fileJsonlines = jsonlines.join('\n');
93
+ }
94
+ else {
95
+ fileJsonlines = jsonlines;
96
+ }
97
+ data.append('file', fileJsonlines, file);
98
+ data.append('purpose', purpose);
99
+ return this.request('/files', 'POST', data);
100
+ }
101
+ getFile(fileId) {
102
+ return this.request(`/files/${fileId}`, 'GET');
103
+ }
104
+ deleteFile(fileId) {
105
+ return this.request(`/files/${fileId}`, 'DELETE');
106
+ }
107
+ finetune(options) {
108
+ return this.request(`/fine-tunes`, 'POST', options);
109
+ }
110
+ getFinetunes() {
111
+ return this.request('/fine-tunes', 'GET').then((r) => r.data);
112
+ }
113
+ getFinetune(finetuneId) {
114
+ return this.request(`/fine-tunes/${finetuneId}`, 'GET');
115
+ }
116
+ cancelFinetune(finetuneId) {
117
+ return this.request(`/fine-tunes/${finetuneId}/cancel`, 'POST');
118
+ }
119
+ getFinetuneEvents(finetuneId) {
120
+ return this.request(`/fine-tunes/${finetuneId}/events`, 'GET').then((r) => r.data);
121
+ }
122
+ createEmbedding(engine, options) {
123
+ return this.request(`/engines/${engine}/embeddings`, 'POST', options);
124
+ }
125
+ async requestRaw(path, method, body) {
126
+ let headers = { ...this.headers };
127
+ if (body instanceof FormData) {
128
+ delete headers['content-type'];
129
+ headers = body.getHeaders(headers);
130
+ }
131
+ else if (!['string', 'undefined'].includes(typeof body)) {
132
+ body = JSON.stringify(body);
133
+ }
134
+ const response = await fetch(this.url + path, {
135
+ headers,
136
+ method,
137
+ body: body,
138
+ });
139
+ if (!response.ok) {
140
+ let errorBody;
141
+ try {
142
+ const { error: { message }, } = (await response.json());
143
+ errorBody = message;
144
+ }
145
+ catch {
146
+ try {
147
+ errorBody = await response.text();
148
+ }
149
+ catch {
150
+ errorBody = 'Failed to get body as text';
151
+ }
152
+ }
153
+ throw new Error(`OpenAI did not return ok: ${response.status} ~ Error body: ${errorBody}`);
154
+ }
155
+ return response;
156
+ }
157
+ async request(path, method, body) {
158
+ const response = await this.requestRaw(path, method, body);
159
+ return response.json();
160
+ }
161
+ eventStreamTransformer() {
162
+ const dataHeader = Buffer.from('data: ');
163
+ return new Transform({
164
+ transform: function (chunk, _, callback) {
165
+ if (chunk.length >= dataHeader.length &&
166
+ dataHeader.compare(chunk, undefined, dataHeader.length) === 0) {
167
+ if (this.prevChunk) {
168
+ const completion = JSON.parse(this.prevChunk.toString());
169
+ this.push(completion.choices[0].text);
170
+ this.prevChunk = undefined;
171
+ }
172
+ chunk = chunk.slice(dataHeader.length);
173
+ }
174
+ this.prevChunk = this.prevChunk ? Buffer.concat([this.prevChunk, chunk]) : chunk;
175
+ callback();
176
+ },
177
+ });
178
+ }
179
+ }
package/dist/types.d.ts CHANGED
@@ -13,6 +13,7 @@ export interface Engine {
13
13
  export interface List<T> {
14
14
  object: 'list';
15
15
  data: T[];
16
+ model?: string;
16
17
  }
17
18
  export interface CompletionRequest {
18
19
  prompt?: string | string[];
@@ -38,7 +39,7 @@ export interface LogProbs {
38
39
  export interface Choice {
39
40
  text: string;
40
41
  index: number;
41
- logprobs: LogProbs;
42
+ logprobs: LogProbs | null;
42
43
  finish_reason: string | null;
43
44
  }
44
45
  export interface Completion {
@@ -48,6 +49,11 @@ export interface Completion {
48
49
  model: string;
49
50
  choices: Choice[];
50
51
  }
52
+ export declare enum ContentLabel {
53
+ Safe = 0,
54
+ Sensitive = 1,
55
+ Unsafe = 2
56
+ }
51
57
  export interface SearchRequest {
52
58
  query: string;
53
59
  documents?: string[];
@@ -168,8 +174,11 @@ export interface FineTune {
168
174
  updated_at: number;
169
175
  user_id: string;
170
176
  }
171
- export declare const enum ContentLabel {
172
- Safe = 0,
173
- Sensitive = 1,
174
- Unsafe = 2
177
+ export interface EmbeddingRequest {
178
+ input: string | string[];
179
+ }
180
+ export interface Embedding {
181
+ object: 'embedding';
182
+ embedding: number[];
183
+ index: number;
175
184
  }
package/dist/types.js CHANGED
@@ -1,12 +1,6 @@
1
- (function (factory) {
2
- if (typeof module === "object" && typeof module.exports === "object") {
3
- var v = factory(require, exports);
4
- if (v !== undefined) module.exports = v;
5
- }
6
- else if (typeof define === "function" && define.amd) {
7
- define(["require", "exports"], factory);
8
- }
9
- })(function (require, exports) {
10
- "use strict";
11
- Object.defineProperty(exports, "__esModule", { value: true });
12
- });
1
+ export var ContentLabel;
2
+ (function (ContentLabel) {
3
+ ContentLabel[ContentLabel["Safe"] = 0] = "Safe";
4
+ ContentLabel[ContentLabel["Sensitive"] = 1] = "Sensitive";
5
+ ContentLabel[ContentLabel["Unsafe"] = 2] = "Unsafe";
6
+ })(ContentLabel || (ContentLabel = {}));
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "openai",
3
- "version": "0.0.8",
3
+ "version": "1.1.0",
4
4
  "description": "Tiny OpenAI API wrapper",
5
+ "type": "module",
5
6
  "main": "dist/index.js",
6
7
  "scripts": {
7
8
  "start": "tsc -d --watch",
@@ -26,20 +27,18 @@
26
27
  },
27
28
  "homepage": "https://github.com/ceifa/openai#readme",
28
29
  "devDependencies": {
29
- "@types/node": "16.7.0",
30
- "@types/node-fetch": "2.5.12",
31
- "@typescript-eslint/eslint-plugin": "4.29.2",
32
- "@typescript-eslint/parser": "4.29.2",
33
- "eslint": "7.32.0",
30
+ "@types/node": "16.11.11",
31
+ "@typescript-eslint/eslint-plugin": "5.5.0",
32
+ "@typescript-eslint/parser": "5.5.0",
33
+ "eslint": "8.3.0",
34
34
  "eslint-config-prettier": "8.3.0",
35
- "eslint-plugin-prettier": "3.4.0",
35
+ "eslint-plugin-prettier": "4.0.0",
36
36
  "eslint-plugin-sort-imports-es6-autofix": "0.6.0",
37
- "prettier": "2.3.2",
38
- "typescript": "4.3.5"
37
+ "prettier": "2.5.0",
38
+ "typescript": "4.5.2"
39
39
  },
40
40
  "dependencies": {
41
41
  "form-data": "4.0.0",
42
- "node-fetch": "2.6.1",
43
- "tslib": "2.3.1"
42
+ "node-fetch": "3.1.0"
44
43
  }
45
44
  }