openai-node
154 строки · 5.0 Кб
1// shouldn't need extension, but Jest's ESM module resolution is broken
2import 'openai/shims/web.mjs';3import OpenAI, { toFile } from 'openai';4import { distance } from 'fastest-levenshtein';5import { ChatCompletion } from 'openai/resources/chat/completions';6
7const url = 'https://audio-samples.github.io/samples/mp3/blizzard_biased/sample-1.mp3';8const filename = 'sample-1.mp3';9
10const correctAnswer =11'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,';12const model = 'whisper-1';13
14const client = new OpenAI();15
16async function typeTests() {17// @ts-expect-error this should error if the `Uploadable` type was resolved correctly18await client.audio.transcriptions.create({ file: { foo: true }, model: 'whisper-1' });19// @ts-expect-error this should error if the `Uploadable` type was resolved correctly20await client.audio.transcriptions.create({ file: null, model: 'whisper-1' });21// @ts-expect-error this should error if the `Uploadable` type was resolved correctly22await client.audio.transcriptions.create({ file: 'test', model: 'whisper-1' });23}
24
25declare global {26namespace jest {27interface Matchers<R> {28toBeSimilarTo(comparedTo: string, expectedDistance: number): R;29}30}31}
32expect.extend({33toBeSimilarTo(received, comparedTo: string, expectedDistance: number) {34const message = () =>35[36`Received: ${JSON.stringify(received)}`,37`Expected: ${JSON.stringify(comparedTo)}`,38`Expected distance: ${expectedDistance}`,39`Received distance: ${actualDistance}`,40].join('\n');41
42const actualDistance = distance(received, comparedTo);43if (actualDistance < expectedDistance) {44return {45message,46pass: true,47};48}49
50return {51message,52pass: false,53};54},55});56
57it(`raw response`, async function () {58const response = await client.chat.completions59.create({60model: 'gpt-4',61messages: [{ role: 'user', content: 'Say this is a test' }],62})63.asResponse();64
65// test that we can use web Response API66const { body } = response;67if (!body) throw new Error('expected response.body to be defined');68
69const reader = body.getReader();70const chunks: Uint8Array[] = [];71let result;72do {73result = await reader.read();74if (!result.done) chunks.push(result.value);75} while (!result.done);76
77reader.releaseLock();78
79let offset = 0;80const merged = new Uint8Array(chunks.reduce((total, chunk) => total + chunk.length, 0));81for (const chunk of chunks) {82merged.set(chunk, offset);83offset += chunk.length;84}85
86const json: ChatCompletion = JSON.parse(new TextDecoder().decode(merged));87expect(json.choices[0]?.message.content || '').toBeSimilarTo('This is a test', 10);88});89
90it(`streaming works`, async function () {91const stream = await client.chat.completions.create({92model: 'gpt-4',93messages: [{ role: 'user', content: 'Say this is a test' }],94stream: true,95});96const chunks = [];97for await (const part of stream) {98chunks.push(part);99}100expect(chunks.map((c) => c.choices[0]?.delta.content || '').join('')).toBeSimilarTo('This is a test', 10);101});102
103if (typeof File !== 'undefined') {104it('handles builtinFile', async function () {105const file = await fetch(url)106.then((x) => x.arrayBuffer())107.then((x) => new File([x], filename));108
109const result = await client.audio.transcriptions.create({ file, model });110expect(result.text).toBeSimilarTo(correctAnswer, 12);111});112}
113
114it('handles Response', async function () {115const file = await fetch(url);116
117const result = await client.audio.transcriptions.create({ file, model });118expect(result.text).toBeSimilarTo(correctAnswer, 12);119});120
121const fineTune = `{"prompt": "<prompt text>", "completion": "<ideal generated text>"}`;122
123describe('toFile', () => {124if (typeof Blob !== 'undefined') {125it('handles builtin Blob', async function () {126const result = await client.files.create({127file: await toFile(new Blob([new TextEncoder().encode(fineTune)]), 'finetune.jsonl'),128purpose: 'fine-tune',129});130expect(result.filename).toEqual('finetune.jsonl');131});132}133it('handles Uint8Array', async function () {134const result = await client.files.create({135file: await toFile(new TextEncoder().encode(fineTune), 'finetune.jsonl'),136purpose: 'fine-tune',137});138expect(result.filename).toEqual('finetune.jsonl');139});140it('handles ArrayBuffer', async function () {141const result = await client.files.create({142file: await toFile(new TextEncoder().encode(fineTune).buffer, 'finetune.jsonl'),143purpose: 'fine-tune',144});145expect(result.filename).toEqual('finetune.jsonl');146});147it('handles DataView', async function () {148const result = await client.files.create({149file: await toFile(new DataView(new TextEncoder().encode(fineTune).buffer), 'finetune.jsonl'),150purpose: 'fine-tune',151});152expect(result.filename).toEqual('finetune.jsonl');153});154});155