UNPKG

@inductiv/node-red-openai-api

Version:

Enhance your Node-RED projects with advanced AI capabilities.

1,562 lines (1,430 loc) 108 kB
"use strict"; // This file is the broad API surface sanity check. // It proves our method wrappers, examples, and help text still line up with the OpenAI SDK contract. const assert = require("node:assert/strict"); const fs = require("node:fs"); const path = require("node:path"); const test = require("node:test"); const adminNoa81EditorMethods = [ ["getOrganizationUsageFileSearchCalls", "retrieve organization usage file search calls", "Retrieve Organization Usage File Search Calls"], ["getOrganizationUsageWebSearchCalls", "retrieve organization usage web search calls", "Retrieve Organization Usage Web Search Calls"], ["getOrganizationDataRetention", "retrieve organization data retention", "Retrieve Organization Data Retention"], ["modifyOrganizationDataRetention", "modify organization data retention", "Modify Organization Data Retention"], ["createOrganizationSpendAlert", "create organization spend alert", "Create Organization Spend Alert"], ["modifyOrganizationSpendAlert", "modify organization spend alert", "Modify Organization Spend Alert"], ["listOrganizationSpendAlerts", "list organization spend alerts", "List Organization Spend Alerts"], ["deleteOrganizationSpendAlert", "delete organization spend alert", "Delete Organization Spend Alert"], ["getProjectDataRetention", "retrieve project data retention", "Retrieve Project Data Retention"], ["modifyProjectDataRetention", "modify project data retention", "Modify Project Data Retention"], ["createProjectSpendAlert", "create project spend alert", "Create Project Spend Alert"], ["modifyProjectSpendAlert", "modify project spend alert", "Modify Project Spend Alert"], ["listProjectSpendAlerts", "list project spend alerts", "List Project Spend Alerts"], ["deleteProjectSpendAlert", "delete project spend alert", "Delete Project Spend Alert"], ["getProjectModelPermissions", "retrieve project model permissions", "Retrieve Project Model Permissions"], ["modifyProjectModelPermissions", "modify project model permissions", "Modify Project Model Permissions"], ["deleteProjectModelPermissions", "delete project model permissions", "Delete Project Model Permissions"], ["getProjectHostedToolPermissions", "retrieve project hosted tool permissions", "Retrieve Project Hosted Tool Permissions"], ["modifyProjectHostedToolPermissions", "modify project hosted tool permissions", "Modify Project Hosted Tool Permissions"], ["getOrganizationUserRole", "retrieve organization user role", "Retrieve Organization User Role"], ["getOrganizationGroup", "retrieve organization group", "Retrieve Organization Group"], ["getOrganizationGroupUser", "retrieve organization group user", "Retrieve Organization Group User"], ["getOrganizationGroupRole", "retrieve organization group role", "Retrieve Organization Group Role"], ["getOrganizationRole", "retrieve organization role", "Retrieve Organization Role"], ["modifyProjectServiceAccount", "modify project service account", "Modify Project Service Account"], ["getProjectUserRole", "retrieve project user role", "Retrieve Project User Role"], ["getProjectGroup", "retrieve project group", "Retrieve Project Group"], ["getProjectGroupRole", "retrieve project group role", "Retrieve Project Group Role"], ["getProjectRole", "retrieve project role", "Retrieve Project Role"], ]; const workloadIdentityAuditEventTypes = [ "workload_identity_provider.created", "workload_identity_provider.updated", "workload_identity_provider.deleted", "workload_identity_provider_mapping.created", "workload_identity_provider_mapping.updated", "workload_identity_provider_mapping.deleted", ]; 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(); } test("responses methods map parse/delete/cancel/compact/input-items/input-tokens to OpenAI SDK", async () => { const calls = []; class FakeOpenAI { constructor(clientParams) { calls.push({ method: "ctor", clientParams }); this.responses = { parse: async (payload) => { calls.push({ method: "responses.parse", payload }); return { id: "resp_parsed", output_parsed: { answer: "done" }, }; }, delete: async (responseId, options) => { calls.push({ method: "responses.delete", responseId, options }); return { id: responseId, deleted: true }; }, cancel: async (responseId, options) => { calls.push({ method: "responses.cancel", responseId, options }); return { id: responseId, status: "cancelled" }; }, compact: async (payload) => { calls.push({ method: "responses.compact", payload }); return { id: "compaction_1", object: "response.compaction" }; }, inputItems: { list: async (responseId, options) => { calls.push({ method: "responses.inputItems.list", responseId, options }); return { data: [{ id: "item_1" }, { id: "item_2" }], }; }, }, inputTokens: { count: async (payload) => { calls.push({ method: "responses.inputTokens.count", payload }); return { object: "response.input_tokens", input_tokens: 42 }; }, }, }; } } await withMockedOpenAI(FakeOpenAI, async () => { const modulePath = require.resolve("../src/responses/methods.js"); delete require.cache[modulePath]; const responsesMethods = require("../src/responses/methods.js"); const clientContext = { clientParams: { apiKey: "sk-test", baseURL: "https://api.example.com/v1" } }; const parsedResponse = await responsesMethods.parseModelResponse.call(clientContext, { payload: { model: "gpt-5.4-mini", input: "Return structured output.", text: { format: { type: "json_schema", name: "result", schema: { type: "object", properties: { answer: { type: "string" }, }, required: ["answer"], additionalProperties: false, }, }, }, }, }); assert.deepEqual(parsedResponse, { id: "resp_parsed", output_parsed: { answer: "done" }, }); const deleteResponse = await responsesMethods.deleteModelResponse.call(clientContext, { payload: { response_id: "resp_123", }, }); assert.deepEqual(deleteResponse, { id: "resp_123", deleted: true }); const cancelResponse = await responsesMethods.cancelModelResponse.call(clientContext, { payload: { response_id: "resp_123", }, }); assert.deepEqual(cancelResponse, { id: "resp_123", status: "cancelled" }); const compactResponse = await responsesMethods.compactModelResponse.call(clientContext, { payload: { model: "gpt-5.2", input: [{ role: "user", content: [{ type: "text", text: "hi" }] }], service_tier: "auto", }, }); assert.deepEqual(compactResponse, { id: "compaction_1", object: "response.compaction", }); const inputItems = await responsesMethods.listInputItems.call(clientContext, { payload: { response_id: "resp_123", order: "desc", include: ["message.input_image.image_url"], }, }); assert.deepEqual(inputItems, [{ id: "item_1" }, { id: "item_2" }]); const inputTokenCount = await responsesMethods.countInputTokens.call(clientContext, { payload: { model: "gpt-4.1-mini", input: "hello", personality: "friendly", }, }); assert.deepEqual(inputTokenCount, { object: "response.input_tokens", input_tokens: 42, }); delete require.cache[modulePath]; }); const responseCalls = calls.filter((entry) => entry.method !== "ctor"); assert.deepEqual(responseCalls, [ { method: "responses.parse", payload: { model: "gpt-5.4-mini", input: "Return structured output.", text: { format: { type: "json_schema", name: "result", schema: { type: "object", properties: { answer: { type: "string" }, }, required: ["answer"], additionalProperties: false, }, }, }, }, }, { method: "responses.delete", responseId: "resp_123", options: {}, }, { method: "responses.cancel", responseId: "resp_123", options: {}, }, { method: "responses.compact", payload: { model: "gpt-5.2", input: [{ role: "user", content: [{ type: "text", text: "hi" }] }], service_tier: "auto", }, }, { method: "responses.inputItems.list", responseId: "resp_123", options: { order: "desc", include: ["message.input_image.image_url"], }, }, { method: "responses.inputTokens.count", payload: { model: "gpt-4.1-mini", input: "hello", personality: "friendly", }, }, ]); }); test("responses create forwards phase, prompt_cache_key, tool_search, defer_loading, computer, and gpt-5.4-mini payloads", async () => { const calls = []; class FakeOpenAI { constructor(clientParams) { calls.push({ method: "ctor", clientParams }); this.responses = { create: async (payload) => { calls.push({ method: "responses.create", payload }); return { id: "resp_123", status: "completed" }; }, }; } } const requestPayload = { model: "gpt-5.4-mini", prompt_cache_key: "responses-agentic-demo-v1", input: [ { type: "additional_tools", role: "developer", id: "item_tools_release_lookup", tools: [ { type: "function", name: "lookup_release_ticket", description: "Look up a release ticket by id.", parameters: { type: "object", properties: { ticket_id: { type: "string" }, }, required: ["ticket_id"], additionalProperties: false, }, strict: true, }, ], }, { type: "message", role: "assistant", phase: "commentary", content: [{ type: "output_text", text: "Planning the response." }], }, { type: "message", role: "user", content: [{ type: "input_text", text: "Summarize the release work." }], }, ], tools: [ { type: "tool_search" }, { type: "function", name: "lookup_release_ticket", description: "Look up a release ticket by id.", parameters: { type: "object", properties: { ticket_id: { type: "string" }, }, required: ["ticket_id"], additionalProperties: false, }, strict: true, defer_loading: true, }, { type: "computer" }, ], }; await withMockedOpenAI(FakeOpenAI, async () => { const modulePath = require.resolve("../src/responses/methods.js"); delete require.cache[modulePath]; const responsesMethods = require("../src/responses/methods.js"); const clientContext = { clientParams: { apiKey: "sk-test", baseURL: "https://api.example.com/v1" } }; const response = await responsesMethods.createModelResponse.call(clientContext, { payload: requestPayload, }); assert.deepEqual(response, { id: "resp_123", status: "completed" }); delete require.cache[modulePath]; }); const createCalls = calls.filter((entry) => entry.method === "responses.create"); assert.deepEqual(createCalls, [ { method: "responses.create", payload: requestPayload, }, ]); }); test("responses create preserves computer keypress actions with keys arrays", async () => { class FakeOpenAI { constructor() { this.responses = { create: async () => ({ id: "resp_computer_1", output: [ { id: "item_1", type: "computer_call", action: { type: "keypress", keys: ["CTRL", "L"], }, }, ], }), }; } } await withMockedOpenAI(FakeOpenAI, async () => { const modulePath = require.resolve("../src/responses/methods.js"); delete require.cache[modulePath]; const responsesMethods = require("../src/responses/methods.js"); const clientContext = { clientParams: { apiKey: "sk-test", baseURL: "https://api.example.com/v1", }, }; const response = await responsesMethods.createModelResponse.call(clientContext, { payload: { model: "gpt-5.4", tools: [{ type: "computer" }], input: "Open the address bar.", }, }); assert.deepEqual(response, { id: "resp_computer_1", output: [ { id: "item_1", type: "computer_call", action: { type: "keypress", keys: ["CTRL", "L"], }, }, ], }); delete require.cache[modulePath]; }); }); test("responses example flows remain valid JSON and cover the documented agentic payload shapes", () => { const phaseExamplePath = path.join( __dirname, "..", "examples", "responses", "phase.json" ); const toolSearchExamplePath = path.join( __dirname, "..", "examples", "responses", "tool-search.json" ); const computerUseExamplePath = path.join( __dirname, "..", "examples", "responses", "computer-use.json" ); const websocketExamplePath = path.join( __dirname, "..", "examples", "responses", "websocket.json" ); const phaseExample = JSON.parse(fs.readFileSync(phaseExamplePath, "utf8")); const toolSearchExample = JSON.parse( fs.readFileSync(toolSearchExamplePath, "utf8") ); const computerUseExample = JSON.parse( fs.readFileSync(computerUseExamplePath, "utf8") ); const websocketExample = JSON.parse( fs.readFileSync(websocketExamplePath, "utf8") ); [phaseExample, toolSearchExample, computerUseExample].forEach((flow) => { assert.ok(Array.isArray(flow)); const openaiNode = flow.find((entry) => entry.type === "OpenAI API"); const commentNodes = flow.filter((entry) => entry.type === "comment"); assert.ok(openaiNode); assert.equal(openaiNode.method, "createModelResponse"); assert.ok(commentNodes.length >= 1); }); const phaseInjectNode = phaseExample.find( (entry) => entry.type === "inject" && entry.name === "Create Phased Response" ); const toolSearchInjectNode = toolSearchExample.find( (entry) => entry.type === "inject" && entry.name === "Create Tool Search Request" ); const computerCreateInjectNode = computerUseExample.find( (entry) => entry.type === "inject" && entry.name === "Create Computer Request" ); const computerFollowupInjectNode = computerUseExample.find( (entry) => entry.type === "inject" && entry.name === "Submit Computer Screenshot (edit placeholders)" ); assert.ok(phaseInjectNode); assert.ok(toolSearchInjectNode); assert.ok(computerCreateInjectNode); assert.ok(computerFollowupInjectNode); const phaseMessage = JSON.parse( phaseInjectNode.props.find((prop) => prop.p === "ai.input[0]").v ); const toolSearchTool = JSON.parse( toolSearchInjectNode.props.find((prop) => prop.p === "ai.tools[0]").v ); const deferredMcpTool = JSON.parse( toolSearchInjectNode.props.find((prop) => prop.p === "ai.tools[1]").v ); const additionalToolsItem = JSON.parse( toolSearchInjectNode.props.find((prop) => prop.p === "ai.input[0]").v ); const computerTool = JSON.parse( computerCreateInjectNode.props.find((prop) => prop.p === "ai.tools[0]").v ); const computerCallOutput = JSON.parse( computerFollowupInjectNode.props.find((prop) => prop.p === "ai.input[0]").v ); assert.equal( phaseInjectNode.props.find((prop) => prop.p === "ai.prompt_cache_key").v, "responses-phase-example-v1" ); assert.equal(phaseMessage.phase, "commentary"); assert.equal(toolSearchTool.type, "tool_search"); assert.equal(deferredMcpTool.defer_loading, true); assert.equal(additionalToolsItem.type, "additional_tools"); assert.equal(additionalToolsItem.role, "developer"); assert.equal(additionalToolsItem.tools[0].name, "lookup_transport_notes"); assert.equal(computerTool.type, "computer"); assert.equal(computerCallOutput.type, "computer_call_output"); assert.equal(computerCallOutput.output.type, "computer_screenshot"); assert.ok(Array.isArray(websocketExample)); const websocketOpenaiNode = websocketExample.find( (entry) => entry.type === "OpenAI API" ); const websocketCommentNodes = websocketExample.filter( (entry) => entry.type === "comment" ); const connectInjectNode = websocketExample.find( (entry) => entry.type === "inject" && entry.name === "Connect Responses WebSocket" ); const sendInjectNode = websocketExample.find( (entry) => entry.type === "inject" && entry.name === "Send response.create Event" ); const closeInjectNode = websocketExample.find( (entry) => entry.type === "inject" && entry.name === "Close Responses WebSocket" ); assert.ok(websocketOpenaiNode); assert.equal(websocketOpenaiNode.method, "manageModelResponseWebSocket"); assert.ok(websocketCommentNodes.length >= 2); assert.ok(connectInjectNode); assert.ok(sendInjectNode); assert.ok(closeInjectNode); assert.equal( connectInjectNode.props.find((prop) => prop.p === "ai.action").v, "connect" ); assert.equal( sendInjectNode.props.find((prop) => prop.p === "ai.action").v, "send" ); assert.equal( closeInjectNode.props.find((prop) => prop.p === "ai.action").v, "close" ); assert.deepEqual( JSON.parse(sendInjectNode.props.find((prop) => prop.p === "ai.event").v), { type: "response.create", model: "gpt-5.4-nano-2026-03-17", input: "Say hello from Responses websocket mode in one sentence.", } ); }); test("realtime example flow remains valid JSON and documents the nested session contract", () => { const realtimeExamplePath = path.join( __dirname, "..", "examples", "realtime", "client-secrets.json" ); const realtimeExample = JSON.parse(fs.readFileSync(realtimeExamplePath, "utf8")); assert.ok(Array.isArray(realtimeExample)); const openaiNode = realtimeExample.find((entry) => entry.type === "OpenAI API"); const realtimeInjectNode = realtimeExample.find( (entry) => entry.type === "inject" && entry.name === "Create Realtime 2 Client Secret" ); const audioInjectNode = realtimeExample.find( (entry) => entry.type === "inject" && entry.name === "Create Audio 1.5 Client Secret" ); const translationInjectNode = realtimeExample.find( (entry) => entry.type === "inject" && entry.name === "Create Translation Client Secret" ); assert.ok(openaiNode); assert.equal(openaiNode.method, "createRealtimeClientSecret"); assert.ok(realtimeInjectNode); assert.ok(audioInjectNode); assert.ok(translationInjectNode); assert.equal( realtimeInjectNode.props.find((prop) => prop.p === "ai.session.type").v, "realtime" ); assert.equal( realtimeInjectNode.props.find((prop) => prop.p === "ai.session.model").v, "gpt-realtime-2" ); assert.equal( realtimeInjectNode.props.find((prop) => prop.p === "ai.session.reasoning.effort").v, "low" ); assert.equal( realtimeInjectNode.props.find((prop) => prop.p === "ai.session.parallel_tool_calls").v, "true" ); assert.equal( audioInjectNode.props.find((prop) => prop.p === "ai.session.type").v, "realtime" ); assert.equal( audioInjectNode.props.find((prop) => prop.p === "ai.session.model").v, "gpt-audio-1.5" ); assert.equal( realtimeInjectNode.props.find((prop) => prop.p === "ai.expires_after.seconds").v, "600" ); assert.equal( translationInjectNode.props.find((prop) => prop.p === "ai.session.type").v, "translation" ); assert.equal( translationInjectNode.props.find((prop) => prop.p === "ai.session.model").v, "gpt-realtime-translate" ); assert.equal( translationInjectNode.props.find((prop) => prop.p === "ai.session.audio.input.noise_reduction.type").v, "near_field" ); assert.equal( translationInjectNode.props.find((prop) => prop.p === "ai.session.audio.input.transcription.model").v, "gpt-realtime-whisper" ); assert.equal( translationInjectNode.props.find((prop) => prop.p === "ai.session.audio.input.transcription.delay").v, "low" ); assert.equal( translationInjectNode.props.find((prop) => prop.p === "ai.session.audio.input.turn_detection").v, "null" ); assert.equal( translationInjectNode.props.find((prop) => prop.p === "ai.session.audio.output.language").v, "es" ); }); test("responses retrieve streams chunks when stream=true", async () => { const calls = []; async function* createFakeStream() { yield { type: "response.in_progress", sequence_number: 1 }; yield { type: "response.completed", sequence_number: 2 }; } class FakeOpenAI { constructor(clientParams) { calls.push({ method: "ctor", clientParams }); this.responses = { retrieve: async (responseId, options) => { calls.push({ method: "responses.retrieve", responseId, options }); if (options.stream) { return createFakeStream(); } return { id: responseId, status: "completed" }; }, }; } } await withMockedOpenAI(FakeOpenAI, async () => { const modulePath = require.resolve("../src/responses/methods.js"); delete require.cache[modulePath]; const responsesMethods = require("../src/responses/methods.js"); const sentMessages = []; const statuses = []; const node = { send: (msg) => sentMessages.push(msg), status: (status) => statuses.push(status), }; const clientContext = { clientParams: { apiKey: "sk-test" } }; const streamResult = await responsesMethods.getModelResponse.call(clientContext, { _node: node, msg: { topic: "t1" }, payload: { response_id: "resp_stream", stream: true, }, }); assert.equal(streamResult, undefined); assert.deepEqual(sentMessages, [ { topic: "t1", payload: { type: "response.in_progress", sequence_number: 1 }, }, { topic: "t1", payload: { type: "response.completed", sequence_number: 2 }, }, ]); assert.deepEqual(statuses, [ { fill: "green", shape: "dot", text: "OpenaiApi.status.streaming", }, {}, ]); const retrieveResponse = await responsesMethods.getModelResponse.call(clientContext, { _node: node, msg: { topic: "t1" }, payload: { response_id: "resp_non_stream", }, }); assert.deepEqual(retrieveResponse, { id: "resp_non_stream", status: "completed" }); delete require.cache[modulePath]; }); const retrieveCalls = calls.filter((entry) => entry.method === "responses.retrieve"); assert.deepEqual(retrieveCalls, [ { method: "responses.retrieve", responseId: "resp_stream", options: { stream: true }, }, { method: "responses.retrieve", responseId: "resp_non_stream", options: {}, }, ]); }); test("responses stream helper emits events and returns the final parsed response", async () => { const calls = []; function createFakeResponseStream() { return { async *[Symbol.asyncIterator]() { yield { type: "response.in_progress", sequence_number: 1 }; yield { type: "response.output_text.delta", sequence_number: 2, delta: "Hello" }; yield { type: "response.completed", sequence_number: 3 }; }, async finalResponse() { return { id: "resp_stream_final", output_parsed: { answer: "Hello" }, }; }, }; } class FakeOpenAI { constructor(clientParams) { calls.push({ method: "ctor", clientParams }); this.responses = { stream: (payload) => { calls.push({ method: "responses.stream", payload }); return createFakeResponseStream(); }, }; } } await withMockedOpenAI(FakeOpenAI, async () => { const modulePath = require.resolve("../src/responses/methods.js"); delete require.cache[modulePath]; const responsesMethods = require("../src/responses/methods.js"); const sentMessages = []; const statuses = []; const node = { send: (msg) => sentMessages.push(msg), status: (status) => statuses.push(status), }; const clientContext = { clientParams: { apiKey: "sk-test" } }; const finalResponse = await responsesMethods.streamModelResponse.call(clientContext, { _node: node, msg: { topic: "stream-helper" }, payload: { model: "gpt-5.4-mini", input: "Say hello from the stream helper.", }, }); assert.deepEqual(finalResponse, { id: "resp_stream_final", output_parsed: { answer: "Hello" }, }); assert.deepEqual(sentMessages, [ { topic: "stream-helper", payload: { type: "response.in_progress", sequence_number: 1 }, }, { topic: "stream-helper", payload: { type: "response.output_text.delta", sequence_number: 2, delta: "Hello" }, }, { topic: "stream-helper", payload: { type: "response.completed", sequence_number: 3 }, }, ]); assert.deepEqual(statuses, [ { fill: "green", shape: "dot", text: "OpenaiApi.status.streaming", }, {}, ]); delete require.cache[modulePath]; }); assert.deepEqual(calls.filter((entry) => entry.method !== "ctor"), [ { method: "responses.stream", payload: { model: "gpt-5.4-mini", input: "Say hello from the stream helper.", }, }, ]); }); test("conversation methods map to OpenAI SDK conversations endpoints", async () => { const calls = []; class FakeOpenAI { constructor(clientParams) { calls.push({ method: "ctor", clientParams }); this.conversations = { create: async (body) => { calls.push({ method: "conversations.create", body }); return { id: "conv_new" }; }, retrieve: async (conversationId, options) => { calls.push({ method: "conversations.retrieve", conversationId, options }); return { id: conversationId }; }, update: async (conversationId, body) => { calls.push({ method: "conversations.update", conversationId, body }); return { id: conversationId, updated: true }; }, delete: async (conversationId, options) => { calls.push({ method: "conversations.delete", conversationId, options }); return { id: conversationId, deleted: true }; }, items: { create: async (conversationId, body) => { calls.push({ method: "conversations.items.create", conversationId, body }); return { id: "item_new" }; }, retrieve: async (itemId, options) => { calls.push({ method: "conversations.items.retrieve", itemId, options }); return { id: itemId }; }, list: async (conversationId, options) => { calls.push({ method: "conversations.items.list", conversationId, options }); return { data: [{ id: "item_1" }, { id: "item_2" }] }; }, delete: async (itemId, options) => { calls.push({ method: "conversations.items.delete", itemId, options }); return { id: itemId, deleted: true }; }, }, }; } } await withMockedOpenAI(FakeOpenAI, async () => { const modulePath = require.resolve("../src/conversations/methods.js"); delete require.cache[modulePath]; const conversationMethods = require("../src/conversations/methods.js"); const clientContext = { clientParams: { apiKey: "sk-test" } }; const created = await conversationMethods.createConversation.call(clientContext, { payload: { metadata: { app: "node-red" } }, }); assert.deepEqual(created, { id: "conv_new" }); const fetched = await conversationMethods.getConversation.call(clientContext, { payload: { conversation_id: "conv_1", include: ["items"] }, }); assert.deepEqual(fetched, { id: "conv_1" }); const updated = await conversationMethods.modifyConversation.call(clientContext, { payload: { conversation_id: "conv_1", metadata: { env: "dev" } }, }); assert.deepEqual(updated, { id: "conv_1", updated: true }); const deleted = await conversationMethods.deleteConversation.call(clientContext, { payload: { conversation_id: "conv_1" }, }); assert.deepEqual(deleted, { id: "conv_1", deleted: true }); const createdItem = await conversationMethods.createConversationItem.call(clientContext, { payload: { conversation_id: "conv_1", items: [ { type: "additional_tools", role: "developer", id: "item_tools_shipping_lookup", tools: [ { type: "function", name: "lookup_shipping_options", description: "Look up available shipping options for an order.", parameters: { type: "object", properties: { order_id: { type: "string" }, }, required: ["order_id"], additionalProperties: false, }, strict: true, }, ], }, { type: "message", role: "assistant", phase: "commentary", content: [{ type: "output_text", text: "hello" }], }, ], }, }); assert.deepEqual(createdItem, { id: "item_new" }); const fetchedItem = await conversationMethods.getConversationItem.call(clientContext, { payload: { conversation_id: "conv_1", item_id: "item_1", }, }); assert.deepEqual(fetchedItem, { id: "item_1" }); const listedItems = await conversationMethods.listConversationItems.call(clientContext, { payload: { conversation_id: "conv_1", limit: 2, }, }); assert.deepEqual(listedItems, [{ id: "item_1" }, { id: "item_2" }]); const deletedItem = await conversationMethods.deleteConversationItem.call(clientContext, { payload: { conversation_id: "conv_1", item_id: "item_1", }, }); assert.deepEqual(deletedItem, { id: "item_1", deleted: true }); delete require.cache[modulePath]; }); assert.equal(calls.some((entry) => entry.method === "conversations.create"), true); assert.deepEqual( calls.find((entry) => entry.method === "conversations.items.create"), { method: "conversations.items.create", conversationId: "conv_1", body: { items: [ { type: "additional_tools", role: "developer", id: "item_tools_shipping_lookup", tools: [ { type: "function", name: "lookup_shipping_options", description: "Look up available shipping options for an order.", parameters: { type: "object", properties: { order_id: { type: "string" }, }, required: ["order_id"], additionalProperties: false, }, strict: true, }, ], }, { type: "message", role: "assistant", phase: "commentary", content: [{ type: "output_text", text: "hello" }], }, ], }, } ); assert.equal(calls.some((entry) => entry.method === "conversations.items.list"), true); assert.equal(calls.some((entry) => entry.method === "conversations.items.delete"), true); }); test("skills methods map to OpenAI SDK skills endpoints", async () => { const calls = []; class FakeOpenAI { constructor(clientParams) { calls.push({ method: "ctor", clientParams }); this.skills = { create: async (body) => { calls.push({ method: "skills.create", body }); return { id: "skill_new" }; }, retrieve: async (skillId, options) => { calls.push({ method: "skills.retrieve", skillId, options }); return { id: skillId }; }, update: async (skillId, body) => { calls.push({ method: "skills.update", skillId, body }); return { id: skillId, updated: true }; }, delete: async (skillId, options) => { calls.push({ method: "skills.delete", skillId, options }); return { id: skillId, deleted: true }; }, list: async (options) => { calls.push({ method: "skills.list", options }); return { data: [{ id: "skill_1" }, { id: "skill_2" }] }; }, content: { retrieve: async (skillId, options) => { calls.push({ method: "skills.content.retrieve", skillId, options }); return { id: skillId, object: "skill.content" }; }, }, versions: { create: async (skillId, body) => { calls.push({ method: "skills.versions.create", skillId, body }); return { id: "skill_version_new", skill_id: skillId }; }, retrieve: async (version, options) => { calls.push({ method: "skills.versions.retrieve", version, options }); return { id: "skill_version_1", version }; }, list: async (skillId, options) => { calls.push({ method: "skills.versions.list", skillId, options }); return { data: [{ id: "sv_1" }, { id: "sv_2" }] }; }, delete: async (version, options) => { calls.push({ method: "skills.versions.delete", version, options }); return { deleted: true, version }; }, content: { retrieve: async (version, options) => { calls.push({ method: "skills.versions.content.retrieve", version, options }); return { object: "skill.version.content", version }; }, }, }, }; } } await withMockedOpenAI(FakeOpenAI, async () => { const modulePath = require.resolve("../src/skills/methods.js"); delete require.cache[modulePath]; const skillMethods = require("../src/skills/methods.js"); const clientContext = { clientParams: { apiKey: "sk-test" } }; const listed = await skillMethods.listSkills.call(clientContext, { payload: { order: "desc", limit: 2 }, }); assert.deepEqual(listed, [{ id: "skill_1" }, { id: "skill_2" }]); const created = await skillMethods.createSkill.call(clientContext, { payload: { files: ["./skill.zip"] }, }); assert.deepEqual(created, { id: "skill_new" }); const fetched = await skillMethods.getSkill.call(clientContext, { payload: { skill_id: "skill_1" }, }); assert.deepEqual(fetched, { id: "skill_1" }); const updated = await skillMethods.modifySkill.call(clientContext, { payload: { skill_id: "skill_1", default_version: "2" }, }); assert.deepEqual(updated, { id: "skill_1", updated: true }); const deleted = await skillMethods.deleteSkill.call(clientContext, { payload: { skill_id: "skill_1" }, }); assert.deepEqual(deleted, { id: "skill_1", deleted: true }); const content = await skillMethods.getSkillContent.call(clientContext, { payload: { skill_id: "skill_1" }, }); assert.deepEqual(content, { id: "skill_1", object: "skill.content" }); const listedVersions = await skillMethods.listSkillVersions.call(clientContext, { payload: { skill_id: "skill_1", order: "asc" }, }); assert.deepEqual(listedVersions, [{ id: "sv_1" }, { id: "sv_2" }]); const createdVersion = await skillMethods.createSkillVersion.call(clientContext, { payload: { skill_id: "skill_1", default: true, files: ["./v2.zip"] }, }); assert.deepEqual(createdVersion, { id: "skill_version_new", skill_id: "skill_1" }); const fetchedVersion = await skillMethods.getSkillVersion.call(clientContext, { payload: { skill_id: "skill_1", version: "2" }, }); assert.deepEqual(fetchedVersion, { id: "skill_version_1", version: "2" }); const deletedVersion = await skillMethods.deleteSkillVersion.call(clientContext, { payload: { skill_id: "skill_1", version: "2" }, }); assert.deepEqual(deletedVersion, { deleted: true, version: "2" }); const versionContent = await skillMethods.getSkillVersionContent.call(clientContext, { payload: { skill_id: "skill_1", version: "2" }, }); assert.deepEqual(versionContent, { object: "skill.version.content", version: "2" }); delete require.cache[modulePath]; }); const skillCalls = calls.filter((entry) => entry.method !== "ctor"); assert.deepEqual(skillCalls, [ { method: "skills.list", options: { order: "desc", limit: 2 }, }, { method: "skills.create", body: { files: ["./skill.zip"] }, }, { method: "skills.retrieve", skillId: "skill_1", options: {}, }, { method: "skills.update", skillId: "skill_1", body: { default_version: "2" }, }, { method: "skills.delete", skillId: "skill_1", options: {}, }, { method: "skills.content.retrieve", skillId: "skill_1", options: {}, }, { method: "skills.versions.list", skillId: "skill_1", options: { order: "asc" }, }, { method: "skills.versions.create", skillId: "skill_1", body: { default: true, files: ["./v2.zip"] }, }, { method: "skills.versions.retrieve", version: "2", options: { skill_id: "skill_1" }, }, { method: "skills.versions.delete", version: "2", options: { skill_id: "skill_1" }, }, { method: "skills.versions.content.retrieve", version: "2", options: { skill_id: "skill_1" }, }, ]); }); test("chatkit methods map to OpenAI SDK beta chatkit endpoints", async () => { const calls = []; class FakeOpenAI { constructor(clientParams) { calls.push({ method: "ctor", clientParams }); this.beta = { chatkit: { sessions: { create: async (body) => { calls.push({ method: "beta.chatkit.sessions.create", body }); return { id: "cksess_1", object: "chatkit.session", user: body.user, }; }, cancel: async (sessionId, options) => { calls.push({ method: "beta.chatkit.sessions.cancel", sessionId, options, }); return { id: sessionId, object: "chatkit.session", status: "cancelled", }; }, }, threads: { retrieve: async (threadId, options) => { calls.push({ method: "beta.chatkit.threads.retrieve", threadId, options, }); return { id: threadId, object: "chatkit.thread" }; }, list: async (options) => { calls.push({ method: "beta.chatkit.threads.list", options }); return { data: [{ id: "cthr_1" }, { id: "cthr_2" }], }; }, delete: async (threadId, options) => { calls.push({ method: "beta.chatkit.threads.delete", threadId, options, }); return { id: threadId, deleted: true }; }, listItems: async (threadId, options) => { calls.push({ method: "beta.chatkit.threads.listItems", threadId, options, }); return { data: [{ id: "item_1" }, { id: "item_2" }], }; }, }, }, }; } } await withMockedOpenAI(FakeOpenAI, async () => { const modulePath = require.resolve("../src/chatkit/methods.js"); delete require.cache[modulePath]; const chatkitMethods = require("../src/chatkit/methods.js"); const clientContext = { clientParams: { apiKey: "sk-test" } }; const createdSession = await chatkitMethods.createChatKitSession.call( clientContext, { payload: { user: "user_123", workflow: { id: "wf_123", version: "3", state_variables: { region: "us", retries: 2, }, }, }, } ); assert.deepEqual(createdSession, { id: "cksess_1", object: "chatkit.session", user: "user_123", }); const cancelledSession = await chatkitMethods.cancelChatKitSession.call( clientContext, { payload: { session_id: "cksess_1", }, } ); assert.deepEqual(cancelledSession, { id: "cksess_1", object: "chatkit.session", status: "cancelled", }); const listedThreads = await chatkitMethods.listChatKitThreads.call( clientContext, { payload: { user: "user_123", limit: 2, }, } ); assert.deepEqual(listedThreads, [{ id: "cthr_1" }, { id: "cthr_2" }]); const fetchedThread = await chatkitMethods.getChatKitThread.call( clientContext, { payload: { thread_id: "cthr_1", }, } ); assert.deepEqual(fetchedThread, { id: "cthr_1", object: "chatkit.thread" }); const deletedThread = await chatkitMethods.deleteChatKitThread.call( clientContext, { payload: { thread_id: "cthr_1", }, } ); assert.deepEqual(deletedThread, { id: "cthr_1", deleted: true }); const threadItems = await chatkitMethods.listChatKitThreadItems.call( clientContext, { payload: { thread_id: "cthr_1", limit: 2, order: "desc", }, } ); assert.deepEqual(threadItems, [{ id: "item_1" }, { id: "item_2" }]); delete require.cache[modulePath]; }); const chatkitCalls = calls.filter((entry) => entry.method !== "ctor"); assert.deepEqual(chatkitCalls, [ { method: "beta.chatkit.sessions.create", body: { user: "user_123", workflow: { id: "wf_123", version: "3", state_variables: { region: "us", retries: 2, }, }, }, }, { method: "beta.chatkit.sessions.cancel", sessionId: "cksess_1", options: {}, }, { method: "beta.chatkit.threads.list", options: { user: "user_123", limit: 2, }, }, { method: "beta.chatkit.threads.retrieve", threadId: "cthr_1", options: {}, }, { method: "beta.chatkit.threads.delete", threadId: "cthr_1", options: {}, }, { method: "beta.chatkit.threads.listItems", threadId: "cthr_1", options: { limit: 2, order: "desc", }, }, ]); }); test("evals methods map to OpenAI SDK evals endpoints", async () => { const calls = []; class FakeOpenAI { constructor(clientParams) { calls.push({ method: "ctor", clientParams }); this.evals = { create: async (body) => { calls.push({ method: "evals.create", body }); return { id: "eval_new" }; }, retrieve: async (evalId, options) => { calls.push({ method: "evals.retrieve", evalId, options }); return { id: evalId }; }, update: async (evalId, body) => { calls.push({ method: "evals.update", evalId, body }); return { id: evalId, updated: true }; }, delete: async (evalId, options) => { calls.push({ method: "evals.delete", evalId, options }); return { id: evalId, deleted: true }; }, list: async (options) => { calls.push({ method: "evals.list", options }); return { data: [{ id: "eval_1" }, { id: "eval_2" }] }; }, runs: { create: async (evalId, body) => { calls.push({ method: "evals.runs.create", evalId, body }); return { id: "run_new", eval_id: evalId }; }, retrieve: async (runId, options) => { calls.push({ method: "evals.runs.retrieve", runId, options }); return { id: runId }; }, list: async (evalId, options) => { calls.push({ method: "evals.runs.list", evalId, options }); return { data: [{ id: "run_1" }, { id: "run_2" }] }; }, delete: async (runId, options) => { calls.push({ method: "evals.runs.delete", runId, options }); return { id: runId, deleted: true }; }, cancel: async (runId, options) => { calls.push({ method: "evals.runs.cancel", runId, options }); return { id: runId, status: "cancelled" }; }, outputItems: { retrieve: async (outputItemId, options) => { calls.push({ method: "evals.runs.outputItems.retrieve", outputItemId, options, }); return { id: outputItemId }; }, list: async (runId, options) => { calls.push({ method: "evals.runs.outputItems.list", runId, options }); return { data: [{ id: "out_1" }, { id: "out_2" }] }; }, }, }, }; } } await withMockedOpenAI(FakeOpenAI, async () => { const modulePath = require.resolve("../src/evals/methods.js"); delete require.cache[modulePath]; const evalMethods = require("../src/evals/methods.js"); const clientContext = { clientParams: { apiKey: "sk-test" } }; const listedEvals = await evalMethods.listEvals.call(clientContext, { payload: { order: "desc", limit: 2 }, }); assert.deepEqual(listedEvals, [{ id: "eval_1" }, { id: "eval_2" }]); const createdEval = await evalMethods.createEval.call(clientContext, { payload: { name: "Support quality eval", data_source_config: { type: "custom", schema: { type: "object" } }, testing_criteria: [], }, }); assert.deepEqual(createdEval, { id: "eval_new" }); const fetchedEval = await evalMethods.getEval.call(clientContext, { payload: { eval_id: "eval_1" }, }); assert.deepEqual(fetchedEval, { id: "eval_1" }); const updatedEval = await evalMethods.modifyEval.call(clientContext, { payload: { eval_id: "eval_1", name: "Updated eval" }, }); assert.deepEqual(updatedEval, { id: "eval_1", updated: true }); const deletedEval = await evalMethods.deleteEval.call(clientContext, { payload: { eval_id: "eval_1" }, }); assert.deepEqual(deletedEval, { id: "eval_1", deleted: true }); const listedRuns = await evalMethods.listEvalRuns.call(clientContext, { payload: { eval_id: "eval_1", limit: 2 }, }); assert.deepEqual(listedRuns, [{ id: "run_1" }, { id: "run_2" }]); const createdRun = await evalMethods.createEvalRun.call(clientContext, { payload: { eval_id: "eval_1", data_source: { type: "jsonl", source: { type: "file_id", id: "file_1" }, }, }, }); assert.deepEqual(createdRun, { id: "run_new", eval_id: "eval_1" }); const fetchedRun = await evalMethods.getEvalRun.call(clientContext, { payload: { eval_id: "eval_1", run_id: "run_1" }, }); assert.deepEqual(fetchedRun, { id: "run_1" }); const cancelledRun = await evalMethods.cancelEvalRun.call(clientContext, { payload: { eval_id: "eval_1", run_id: "run_1" }, }); assert.deepEqual(cancelledRun, { id: "run_1", status: "cancelled" }); const deletedRun = await evalMethods.deleteEvalRun.call(clientContext, { payload: { eval_id: "eval_1", run_id: "run_1" }, }); assert.deepEqual(deletedRun, { id: "run_1", deleted: true }); const listedOutputItems = await evalMethods.listEvalRunOutputItems.call(clientContext, { payload: { eval_id: "eval_1", run_id: "run_1", limit: 1 }, }); assert.deepEqual(lis