openai-node
194 строки · 6.3 Кб
1import 'openai/shims/node';
2import OpenAI, { toFile } from 'openai';
3import { TranscriptionCreateParams } from 'openai/resources/audio/transcriptions';
4import fetch from 'node-fetch';
5import { File as FormDataFile, Blob as FormDataBlob } from 'formdata-node';
6import * as fs from 'fs';
7import { distance } from 'fastest-levenshtein';
8import { ChatCompletion } from 'openai/resources/chat/completions';
9
10const url = 'https://audio-samples.github.io/samples/mp3/blizzard_biased/sample-1.mp3';
11const filename = 'sample-1.mp3';
12
13const correctAnswer =
14'It was anxious to find him no one that expectation of a man who were giving his father enjoyment. But he was avoided in sight in the minister to which indeed,';
15const model = 'whisper-1';
16
17const client = new OpenAI();
18
19async function typeTests() {
20// @ts-expect-error this should error if the `Uploadable` type was resolved correctly
21await client.audio.transcriptions.create({ file: { foo: true }, model: 'whisper-1' });
22// @ts-expect-error this should error if the `Uploadable` type was resolved correctly
23await client.audio.transcriptions.create({ file: null, model: 'whisper-1' });
24// @ts-expect-error this should error if the `Uploadable` type was resolved correctly
25await client.audio.transcriptions.create({ file: 'test', model: 'whisper-1' });
26}
27
28declare global {
29namespace jest {
30interface Matchers<R> {
31toBeSimilarTo(comparedTo: string, expectedDistance: number): R;
32}
33}
34}
35expect.extend({
36toBeSimilarTo(received, comparedTo: string, expectedDistance: number) {
37const message = () =>
38[
39`Received: ${JSON.stringify(received)}`,
40`Expected: ${JSON.stringify(comparedTo)}`,
41`Expected distance: ${expectedDistance}`,
42`Received distance: ${actualDistance}`,
43].join('\n');
44
45const actualDistance = distance(received, comparedTo);
46if (actualDistance < expectedDistance) {
47return {
48message,
49pass: true,
50};
51}
52
53return {
54message,
55pass: false,
56};
57},
58});
59
60it(`raw response`, async function () {
61const response = await client.chat.completions
62.create({
63model: 'gpt-4',
64messages: [{ role: 'user', content: 'Say this is a test' }],
65})
66.asResponse();
67
68// test that we can use node-fetch Response API
69const chunks: string[] = [];
70response.body.on('data', (chunk) => chunks.push(chunk));
71await new Promise<void>((resolve, reject) => {
72response.body.once('end', resolve);
73response.body.once('error', reject);
74});
75const json: ChatCompletion = JSON.parse(chunks.join(''));
76expect(json.choices[0]?.message.content || '').toBeSimilarTo('This is a test', 10);
77});
78
79it(`streaming works`, async function () {
80const stream = await client.chat.completions.create({
81model: 'gpt-4',
82messages: [{ role: 'user', content: 'Say this is a test' }],
83stream: true,
84});
85const chunks = [];
86for await (const part of stream) {
87chunks.push(part);
88}
89expect(chunks.map((c) => c.choices[0]?.delta.content || '').join('')).toBeSimilarTo('This is a test', 10);
90});
91
92it('handles formdata-node File', async function () {
93const file = await fetch(url)
94.then((x) => x.arrayBuffer())
95.then((x) => new FormDataFile([x], filename));
96
97const params: TranscriptionCreateParams = { file, model };
98
99const result = await client.audio.transcriptions.create(params);
100expect(result.text).toBeSimilarTo(correctAnswer, 12);
101});
102
103// @ts-ignore avoid DOM lib for testing purposes
104if (typeof File !== 'undefined') {
105it('handles builtinFile', async function () {
106const file = await fetch(url)
107.then((x) => x.arrayBuffer())
108// @ts-ignore avoid DOM lib for testing purposes
109.then((x) => new File([x], filename));
110
111const result = await client.audio.transcriptions.create({ file, model });
112expect(result.text).toBeSimilarTo(correctAnswer, 12);
113});
114}
115
116it('handles Response', async function () {
117const file = await fetch(url);
118
119const result = await client.audio.transcriptions.create({ file, model });
120expect(result.text).toBeSimilarTo(correctAnswer, 12);
121});
122
123it('handles fs.ReadStream', async function () {
124const result = await client.audio.transcriptions.create({
125file: fs.createReadStream('sample1.mp3'),
126model,
127});
128expect(result.text).toBeSimilarTo(correctAnswer, 12);
129});
130
131const fineTune = `{"prompt": "<prompt text>", "completion": "<ideal generated text>"}`;
132
133describe('toFile', () => {
134it('handles form-data Blob', async function () {
135const result = await client.files.create({
136file: await toFile(
137new FormDataBlob([
138// @ts-ignore avoid DOM lib for testing purposes
139new TextEncoder().encode(fineTune),
140]),
141'finetune.jsonl',
142),
143purpose: 'fine-tune',
144});
145expect(result.filename).toEqual('finetune.jsonl');
146});
147// @ts-ignore avoid DOM lib for testing purposes
148if (typeof Blob !== 'undefined') {
149it('handles builtin Blob', async function () {
150const result = await client.files.create({
151file: await toFile(
152// @ts-ignore avoid DOM lib for testing purposes
153new Blob([new TextEncoder().encode(fineTune)]),
154'finetune.jsonl',
155),
156purpose: 'fine-tune',
157});
158expect(result.filename).toEqual('finetune.jsonl');
159});
160}
161it('handles Uint8Array', async function () {
162const result = await client.files.create({
163file: await toFile(
164// @ts-ignore avoid DOM lib for testing purposes
165new TextEncoder().encode(fineTune),
166'finetune.jsonl',
167),
168purpose: 'fine-tune',
169});
170expect(result.filename).toEqual('finetune.jsonl');
171});
172it('handles ArrayBuffer', async function () {
173const result = await client.files.create({
174file: await toFile(
175// @ts-ignore avoid DOM lib for testing purposes
176new TextEncoder().encode(fineTune).buffer,
177'finetune.jsonl',
178),
179purpose: 'fine-tune',
180});
181expect(result.filename).toEqual('finetune.jsonl');
182});
183it('handles DataView', async function () {
184const result = await client.files.create({
185file: await toFile(
186// @ts-ignore avoid DOM lib for testing purposes
187new DataView(new TextEncoder().encode(fineTune).buffer),
188'finetune.jsonl',
189),
190purpose: 'fine-tune',
191});
192expect(result.filename).toEqual('finetune.jsonl');
193});
194});
195