UNPKG

@inductiv/node-red-openai-api

Version:

Enhance your Node-RED projects with advanced AI capabilities.

559 lines (519 loc) 16.9 kB
"use strict"; // This file keeps the wider Vector Store family contract honest. // It checks the remaining Vector Store Files and File Batch surfaces against the current SDK so the picker and wrappers stay aligned together. const assert = require("node:assert/strict"); const fs = require("node:fs"); const os = require("node:os"); const path = require("node:path"); const test = require("node:test"); function withMockedOpenAI(FakeOpenAI, callback) { const openaiModule = require("openai"); const originalDescriptor = Object.getOwnPropertyDescriptor(openaiModule, "OpenAI"); Object.defineProperty(openaiModule, "OpenAI", { value: FakeOpenAI, configurable: true, enumerable: true, writable: true, }); const run = async () => { try { return await callback(); } finally { if (originalDescriptor) { Object.defineProperty(openaiModule, "OpenAI", originalDescriptor); } } }; return run(); } function withMockedCreateReadStream(callback) { const originalCreateReadStream = fs.createReadStream; fs.createReadStream = (filePath) => ({ path: filePath, destroy() {}, }); const run = async () => { try { return await callback(); } finally { fs.createReadStream = originalCreateReadStream; } }; return run(); } const locale = JSON.parse( fs.readFileSync(path.join(__dirname, "..", "locales", "en-US", "node.json"), "utf8") ); test("vector store file methods map to the current OpenAI SDK surface", async () => { const calls = []; const tempFilePath = path.join( os.tmpdir(), `node-red-openai-api-vector-store-${process.pid}.txt` ); fs.writeFileSync(tempFilePath, "vector store helper upload"); class FakeOpenAI { constructor(clientParams) { calls.push({ method: "ctor", clientParams }); this.vectorStores = { files: { create: async (vectorStoreId, body) => { calls.push({ method: "vectorStores.files.create", vectorStoreId, body }); return { id: "vsf_create", vector_store_id: vectorStoreId }; }, createAndPoll: async (vectorStoreId, body, options) => { calls.push({ method: "vectorStores.files.createAndPoll", vectorStoreId, body, options, }); return { id: "vsf_create_poll", status: "completed" }; }, upload: async (vectorStoreId, file, options) => { calls.push({ method: "vectorStores.files.upload", vectorStoreId, filePath: file.path, options, }); file.destroy(); return { id: "vsf_upload", vector_store_id: vectorStoreId }; }, uploadAndPoll: async (vectorStoreId, file, options) => { calls.push({ method: "vectorStores.files.uploadAndPoll", vectorStoreId, filePath: file.path, options, }); file.destroy(); return { id: "vsf_upload_poll", status: "completed" }; }, poll: async (vectorStoreId, fileId, options) => { calls.push({ method: "vectorStores.files.poll", vectorStoreId, fileId, options, }); return { id: fileId, status: "completed" }; }, retrieve: async (fileId, params) => { calls.push({ method: "vectorStores.files.retrieve", fileId, params }); return { id: fileId, vector_store_id: params.vector_store_id }; }, update: async (fileId, body) => { calls.push({ method: "vectorStores.files.update", fileId, body }); return { id: fileId, attributes: body.attributes }; }, content: async (fileId, params) => { calls.push({ method: "vectorStores.files.content", fileId, params }); return { data: [ { type: "text", text: "first chunk" }, { type: "text", text: "second chunk" }, ], }; }, del: async (fileId, params) => { calls.push({ method: "vectorStores.files.del", fileId, params }); return { id: fileId, deleted: true }; }, }, }; } } try { await withMockedCreateReadStream(async () => { await withMockedOpenAI(FakeOpenAI, async () => { const modulePath = require.resolve("../src/vector-store-files/methods.js"); delete require.cache[modulePath]; const vectorStoreFileMethods = require("../src/vector-store-files/methods.js"); const clientContext = { clientParams: { apiKey: "sk-test", baseURL: "https://api.example.com/v1", }, }; await vectorStoreFileMethods.createVectorStoreFile.call(clientContext, { payload: { vector_store_id: "vs_123", file_id: "file_123", attributes: { team: "platform" }, chunking_strategy: { type: "auto" }, }, }); await vectorStoreFileMethods.createAndPollVectorStoreFile.call(clientContext, { payload: { vector_store_id: "vs_123", file_id: "file_123", attributes: { team: "platform" }, pollIntervalMs: 250, }, }); await vectorStoreFileMethods.uploadVectorStoreFile.call(clientContext, { payload: { vector_store_id: "vs_123", file: tempFilePath, }, }); await vectorStoreFileMethods.uploadAndPollVectorStoreFile.call(clientContext, { payload: { vector_store_id: "vs_123", file: tempFilePath, pollIntervalMs: 500, }, }); await vectorStoreFileMethods.pollVectorStoreFile.call(clientContext, { payload: { vector_store_id: "vs_123", file_id: "file_123", pollIntervalMs: 750, }, }); await vectorStoreFileMethods.retrieveVectorStoreFile.call(clientContext, { payload: { vector_store_id: "vs_123", file_id: "file_123", }, }); await vectorStoreFileMethods.modifyVectorStoreFile.call(clientContext, { payload: { vector_store_id: "vs_123", file_id: "file_123", attributes: { team: "integrations", priority: 1 }, }, }); const content = await vectorStoreFileMethods.getVectorStoreFileContent.call( clientContext, { payload: { vector_store_id: "vs_123", file_id: "file_123", }, } ); assert.deepEqual(content, [ { type: "text", text: "first chunk" }, { type: "text", text: "second chunk" }, ]); await vectorStoreFileMethods.deleteVectorStoreFile.call(clientContext, { payload: { vector_store_id: "vs_123", file_id: "file_123", }, }); delete require.cache[modulePath]; }); }); } finally { fs.rmSync(tempFilePath, { force: true }); } assert.deepEqual( calls.filter((entry) => entry.method !== "ctor"), [ { method: "vectorStores.files.create", vectorStoreId: "vs_123", body: { file_id: "file_123", attributes: { team: "platform" }, chunking_strategy: { type: "auto" }, }, }, { method: "vectorStores.files.createAndPoll", vectorStoreId: "vs_123", body: { file_id: "file_123", attributes: { team: "platform" }, }, options: { pollIntervalMs: 250 }, }, { method: "vectorStores.files.upload", vectorStoreId: "vs_123", filePath: tempFilePath, options: {}, }, { method: "vectorStores.files.uploadAndPoll", vectorStoreId: "vs_123", filePath: tempFilePath, options: { pollIntervalMs: 500 }, }, { method: "vectorStores.files.poll", vectorStoreId: "vs_123", fileId: "file_123", options: { pollIntervalMs: 750 }, }, { method: "vectorStores.files.retrieve", fileId: "file_123", params: { vector_store_id: "vs_123", }, }, { method: "vectorStores.files.update", fileId: "file_123", body: { vector_store_id: "vs_123", attributes: { team: "integrations", priority: 1 }, }, }, { method: "vectorStores.files.content", fileId: "file_123", params: { vector_store_id: "vs_123", }, }, { method: "vectorStores.files.del", fileId: "file_123", params: { vector_store_id: "vs_123", }, }, ] ); }); test("vector store file batch helpers map to the current OpenAI SDK surface", async () => { const calls = []; const firstTempFilePath = path.join( os.tmpdir(), `node-red-openai-api-vector-batch-1-${process.pid}.txt` ); const secondTempFilePath = path.join( os.tmpdir(), `node-red-openai-api-vector-batch-2-${process.pid}.txt` ); fs.writeFileSync(firstTempFilePath, "vector store batch helper upload one"); fs.writeFileSync(secondTempFilePath, "vector store batch helper upload two"); class FakeOpenAI { constructor(clientParams) { calls.push({ method: "ctor", clientParams }); this.vectorStores = { fileBatches: { create: async (vectorStoreId, body) => { calls.push({ method: "vectorStores.fileBatches.create", vectorStoreId, body }); return { id: "vsfb_create", vector_store_id: vectorStoreId }; }, createAndPoll: async (vectorStoreId, body, options) => { calls.push({ method: "vectorStores.fileBatches.createAndPoll", vectorStoreId, body, options, }); return { id: "vsfb_create_poll", status: "completed" }; }, retrieve: async (batchId, params) => { calls.push({ method: "vectorStores.fileBatches.retrieve", batchId, params, }); return { id: batchId, vector_store_id: params.vector_store_id }; }, poll: async (vectorStoreId, batchId, options) => { calls.push({ method: "vectorStores.fileBatches.poll", vectorStoreId, batchId, options, }); return { id: batchId, status: "completed" }; }, cancel: async (batchId, params) => { calls.push({ method: "vectorStores.fileBatches.cancel", batchId, params, }); return { id: batchId, status: "cancelled" }; }, listFiles: async (batchId, params) => { calls.push({ method: "vectorStores.fileBatches.listFiles", batchId, params, }); return { data: [{ id: "vsf_1" }, { id: "vsf_2" }], }; }, uploadAndPoll: async (vectorStoreId, payload, options) => { calls.push({ method: "vectorStores.fileBatches.uploadAndPoll", vectorStoreId, filePaths: payload.files.map((file) => file.path), fileIds: payload.fileIds, options, }); payload.files.forEach((file) => file.destroy()); return { id: "vsfb_upload_poll", status: "completed" }; }, }, }; } } try { await withMockedCreateReadStream(async () => { await withMockedOpenAI(FakeOpenAI, async () => { const modulePath = require.resolve("../src/vector-store-file-batches/methods.js"); delete require.cache[modulePath]; const vectorStoreFileBatchMethods = require("../src/vector-store-file-batches/methods.js"); const clientContext = { clientParams: { apiKey: "sk-test", baseURL: "https://api.example.com/v1", }, }; await vectorStoreFileBatchMethods.createVectorStoreFileBatch.call(clientContext, { payload: { vector_store_id: "vs_123", files: [ { file_id: "file_1", attributes: { category: "release" }, }, ], }, }); await vectorStoreFileBatchMethods.createAndPollVectorStoreFileBatch.call(clientContext, { payload: { vector_store_id: "vs_123", file_ids: ["file_1", "file_2"], attributes: { category: "release" }, pollIntervalMs: 200, }, }); await vectorStoreFileBatchMethods.retrieveVectorStoreFileBatch.call(clientContext, { payload: { vector_store_id: "vs_123", batch_id: "vsfb_123", }, }); await vectorStoreFileBatchMethods.pollVectorStoreFileBatch.call(clientContext, { payload: { vector_store_id: "vs_123", batch_id: "vsfb_123", pollIntervalMs: 400, }, }); await vectorStoreFileBatchMethods.cancelVectorStoreFileBatch.call(clientContext, { payload: { vector_store_id: "vs_123", batch_id: "vsfb_123", }, }); const listedFiles = await vectorStoreFileBatchMethods.listVectorStoreBatchFiles.call( clientContext, { payload: { vector_store_id: "vs_123", batch_id: "vsfb_123", filter: "completed", }, } ); assert.deepEqual(listedFiles, [{ id: "vsf_1" }, { id: "vsf_2" }]); await vectorStoreFileBatchMethods.uploadAndPollVectorStoreFileBatch.call(clientContext, { payload: { vector_store_id: "vs_123", files: [firstTempFilePath, secondTempFilePath], file_ids: ["file_existing"], pollIntervalMs: 600, maxConcurrency: 3, }, }); delete require.cache[modulePath]; }); }); } finally { fs.rmSync(firstTempFilePath, { force: true }); fs.rmSync(secondTempFilePath, { force: true }); } assert.deepEqual( calls.filter((entry) => entry.method !== "ctor"), [ { method: "vectorStores.fileBatches.create", vectorStoreId: "vs_123", body: { files: [ { file_id: "file_1", attributes: { category: "release" }, }, ], }, }, { method: "vectorStores.fileBatches.createAndPoll", vectorStoreId: "vs_123", body: { file_ids: ["file_1", "file_2"], attributes: { category: "release" }, }, options: { pollIntervalMs: 200 }, }, { method: "vectorStores.fileBatches.retrieve", batchId: "vsfb_123", params: { vector_store_id: "vs_123", }, }, { method: "vectorStores.fileBatches.poll", vectorStoreId: "vs_123", batchId: "vsfb_123", options: { pollIntervalMs: 400 }, }, { method: "vectorStores.fileBatches.cancel", batchId: "vsfb_123", params: { vector_store_id: "vs_123", }, }, { method: "vectorStores.fileBatches.listFiles", batchId: "vsfb_123", params: { vector_store_id: "vs_123", filter: "completed", }, }, { method: "vectorStores.fileBatches.uploadAndPoll", vectorStoreId: "vs_123", filePaths: [firstTempFilePath, secondTempFilePath], fileIds: ["file_existing"], options: { pollIntervalMs: 600, maxConcurrency: 3 }, }, ] ); }); test("vector store file and file-batch pickers expose the current SDK methods", () => { assert.equal( locale.OpenaiApi.parameters.modifyVectorStoreFile, "modify vector store file" ); assert.equal( locale.OpenaiApi.parameters.getVectorStoreFileContent, "get vector store file content" ); assert.equal( locale.OpenaiApi.parameters.createAndPollVectorStoreFileBatch, "create and poll vector store file batch" ); assert.equal( locale.OpenaiApi.parameters.pollVectorStoreFileBatch, "poll vector store file batch" ); });