@inductiv/node-red-openai-api 1.103.0 → 6.27.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 +213 -86
- package/examples/realtime/client-secrets.json +182 -0
- package/examples/responses/computer-use.json +142 -0
- package/examples/responses/mcp.json +1 -1
- package/examples/responses/phase.json +102 -0
- package/examples/responses/tool-search.json +107 -0
- package/examples/responses/websocket.json +172 -0
- package/internals/openai-api-features-v6.23.0-v6.27.0.md +96 -0
- package/lib.js +12696 -15003
- package/locales/en-US/node.json +50 -1
- package/node.html +1723 -1012
- package/node.js +204 -54
- package/package.json +9 -7
- package/src/assistants/help.html +1 -77
- package/src/audio/help.html +1 -37
- package/src/batch/help.html +3 -17
- package/src/chat/help.html +11 -89
- package/src/container-files/help.html +1 -27
- package/src/containers/help.html +8 -18
- package/src/conversations/help.html +135 -0
- package/src/conversations/methods.js +73 -0
- package/src/conversations/template.html +10 -0
- package/src/embeddings/help.html +1 -11
- package/src/evals/help.html +249 -0
- package/src/evals/methods.js +114 -0
- package/src/evals/template.html +14 -0
- package/src/files/help.html +4 -17
- package/src/fine-tuning/help.html +1 -35
- package/src/images/help.html +1 -45
- package/src/lib.js +53 -1
- package/src/messages/help.html +19 -39
- package/src/messages/methods.js +13 -0
- package/src/messages/template.html +7 -18
- package/src/models/help.html +1 -5
- package/src/moderations/help.html +1 -5
- package/src/node.html +126 -37
- package/src/realtime/help.html +209 -0
- package/src/realtime/methods.js +45 -0
- package/src/realtime/template.html +7 -0
- package/src/responses/help.html +286 -63
- package/src/responses/methods.js +234 -16
- package/src/responses/template.html +21 -1
- package/src/responses/websocket.js +150 -0
- package/src/runs/help.html +1 -123
- package/src/skills/help.html +183 -0
- package/src/skills/methods.js +99 -0
- package/src/skills/template.html +13 -0
- package/src/threads/help.html +1 -15
- package/src/uploads/help.html +1 -21
- package/src/vector-store-file-batches/help.html +1 -27
- package/src/vector-store-file-batches/methods.js +5 -5
- package/src/vector-store-files/help.html +1 -25
- package/src/vector-store-files/methods.js +4 -7
- package/src/vector-stores/help.html +2 -31
- package/src/vector-stores/methods.js +5 -11
- package/src/vector-stores/template.html +7 -22
- package/src/videos/help.html +113 -0
- package/src/videos/methods.js +50 -0
- package/src/videos/template.html +8 -0
- package/src/webhooks/help.html +61 -0
- package/src/webhooks/methods.js +40 -0
- package/src/webhooks/template.html +4 -0
- package/test/openai-methods-mapping.test.js +1559 -0
- package/test/openai-node-auth-routing.test.js +206 -0
- package/test/openai-responses-websocket.test.js +472 -0
- package/test/service-host-editor-template.test.js +56 -0
- package/test/service-host-node.test.js +185 -0
- package/test/services.test.js +150 -0
- package/test/utils.test.js +78 -0
|
@@ -0,0 +1,1559 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// This file is the broad API surface sanity check.
|
|
4
|
+
// It proves our method wrappers, examples, and help text still line up with the OpenAI SDK contract.
|
|
5
|
+
|
|
6
|
+
const assert = require("node:assert/strict");
|
|
7
|
+
const fs = require("node:fs");
|
|
8
|
+
const path = require("node:path");
|
|
9
|
+
const test = require("node:test");
|
|
10
|
+
|
|
11
|
+
function withMockedOpenAI(FakeOpenAI, callback) {
|
|
12
|
+
const openaiModule = require("openai");
|
|
13
|
+
const originalDescriptor = Object.getOwnPropertyDescriptor(openaiModule, "OpenAI");
|
|
14
|
+
|
|
15
|
+
Object.defineProperty(openaiModule, "OpenAI", {
|
|
16
|
+
value: FakeOpenAI,
|
|
17
|
+
configurable: true,
|
|
18
|
+
enumerable: true,
|
|
19
|
+
writable: true,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const run = async () => {
|
|
23
|
+
try {
|
|
24
|
+
return await callback();
|
|
25
|
+
} finally {
|
|
26
|
+
if (originalDescriptor) {
|
|
27
|
+
Object.defineProperty(openaiModule, "OpenAI", originalDescriptor);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
return run();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
test("responses methods map delete/cancel/compact/input-items/input-tokens to OpenAI SDK", async () => {
|
|
36
|
+
const calls = [];
|
|
37
|
+
|
|
38
|
+
class FakeOpenAI {
|
|
39
|
+
constructor(clientParams) {
|
|
40
|
+
calls.push({ method: "ctor", clientParams });
|
|
41
|
+
this.responses = {
|
|
42
|
+
delete: async (responseId, options) => {
|
|
43
|
+
calls.push({ method: "responses.delete", responseId, options });
|
|
44
|
+
return { id: responseId, deleted: true };
|
|
45
|
+
},
|
|
46
|
+
cancel: async (responseId, options) => {
|
|
47
|
+
calls.push({ method: "responses.cancel", responseId, options });
|
|
48
|
+
return { id: responseId, status: "cancelled" };
|
|
49
|
+
},
|
|
50
|
+
compact: async (payload) => {
|
|
51
|
+
calls.push({ method: "responses.compact", payload });
|
|
52
|
+
return { id: "compaction_1", object: "response.compaction" };
|
|
53
|
+
},
|
|
54
|
+
inputItems: {
|
|
55
|
+
list: async (responseId, options) => {
|
|
56
|
+
calls.push({ method: "responses.inputItems.list", responseId, options });
|
|
57
|
+
return {
|
|
58
|
+
data: [{ id: "item_1" }, { id: "item_2" }],
|
|
59
|
+
};
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
inputTokens: {
|
|
63
|
+
count: async (payload) => {
|
|
64
|
+
calls.push({ method: "responses.inputTokens.count", payload });
|
|
65
|
+
return { object: "response.input_tokens", input_tokens: 42 };
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
await withMockedOpenAI(FakeOpenAI, async () => {
|
|
73
|
+
const modulePath = require.resolve("../src/responses/methods.js");
|
|
74
|
+
delete require.cache[modulePath];
|
|
75
|
+
const responsesMethods = require("../src/responses/methods.js");
|
|
76
|
+
|
|
77
|
+
const clientContext = { clientParams: { apiKey: "sk-test", baseURL: "https://api.example.com/v1" } };
|
|
78
|
+
|
|
79
|
+
const deleteResponse = await responsesMethods.deleteModelResponse.call(clientContext, {
|
|
80
|
+
payload: {
|
|
81
|
+
response_id: "resp_123",
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
assert.deepEqual(deleteResponse, { id: "resp_123", deleted: true });
|
|
85
|
+
|
|
86
|
+
const cancelResponse = await responsesMethods.cancelModelResponse.call(clientContext, {
|
|
87
|
+
payload: {
|
|
88
|
+
response_id: "resp_123",
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
assert.deepEqual(cancelResponse, { id: "resp_123", status: "cancelled" });
|
|
92
|
+
|
|
93
|
+
const compactResponse = await responsesMethods.compactModelResponse.call(clientContext, {
|
|
94
|
+
payload: {
|
|
95
|
+
model: "gpt-5.2",
|
|
96
|
+
input: [{ role: "user", content: [{ type: "text", text: "hi" }] }],
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
assert.deepEqual(compactResponse, {
|
|
100
|
+
id: "compaction_1",
|
|
101
|
+
object: "response.compaction",
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const inputItems = await responsesMethods.listInputItems.call(clientContext, {
|
|
105
|
+
payload: {
|
|
106
|
+
response_id: "resp_123",
|
|
107
|
+
order: "desc",
|
|
108
|
+
include: ["message.input_image.image_url"],
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
assert.deepEqual(inputItems, [{ id: "item_1" }, { id: "item_2" }]);
|
|
112
|
+
|
|
113
|
+
const inputTokenCount = await responsesMethods.countInputTokens.call(clientContext, {
|
|
114
|
+
payload: {
|
|
115
|
+
model: "gpt-4.1-mini",
|
|
116
|
+
input: "hello",
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
assert.deepEqual(inputTokenCount, {
|
|
120
|
+
object: "response.input_tokens",
|
|
121
|
+
input_tokens: 42,
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
delete require.cache[modulePath];
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
const responseCalls = calls.filter((entry) => entry.method !== "ctor");
|
|
128
|
+
assert.deepEqual(responseCalls, [
|
|
129
|
+
{
|
|
130
|
+
method: "responses.delete",
|
|
131
|
+
responseId: "resp_123",
|
|
132
|
+
options: {},
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
method: "responses.cancel",
|
|
136
|
+
responseId: "resp_123",
|
|
137
|
+
options: {},
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
method: "responses.compact",
|
|
141
|
+
payload: {
|
|
142
|
+
model: "gpt-5.2",
|
|
143
|
+
input: [{ role: "user", content: [{ type: "text", text: "hi" }] }],
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
method: "responses.inputItems.list",
|
|
148
|
+
responseId: "resp_123",
|
|
149
|
+
options: {
|
|
150
|
+
order: "desc",
|
|
151
|
+
include: ["message.input_image.image_url"],
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
method: "responses.inputTokens.count",
|
|
156
|
+
payload: {
|
|
157
|
+
model: "gpt-4.1-mini",
|
|
158
|
+
input: "hello",
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
]);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test("responses create forwards phase, prompt_cache_key, tool_search, defer_loading, computer, and gpt-5.4 payloads", async () => {
|
|
165
|
+
const calls = [];
|
|
166
|
+
|
|
167
|
+
class FakeOpenAI {
|
|
168
|
+
constructor(clientParams) {
|
|
169
|
+
calls.push({ method: "ctor", clientParams });
|
|
170
|
+
this.responses = {
|
|
171
|
+
create: async (payload) => {
|
|
172
|
+
calls.push({ method: "responses.create", payload });
|
|
173
|
+
return { id: "resp_123", status: "completed" };
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const requestPayload = {
|
|
180
|
+
model: "gpt-5.4",
|
|
181
|
+
prompt_cache_key: "responses-agentic-demo-v1",
|
|
182
|
+
input: [
|
|
183
|
+
{
|
|
184
|
+
type: "message",
|
|
185
|
+
role: "assistant",
|
|
186
|
+
phase: "commentary",
|
|
187
|
+
content: [{ type: "output_text", text: "Planning the response." }],
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
type: "message",
|
|
191
|
+
role: "user",
|
|
192
|
+
content: [{ type: "input_text", text: "Summarize the release work." }],
|
|
193
|
+
},
|
|
194
|
+
],
|
|
195
|
+
tools: [
|
|
196
|
+
{ type: "tool_search" },
|
|
197
|
+
{
|
|
198
|
+
type: "function",
|
|
199
|
+
name: "lookup_release_ticket",
|
|
200
|
+
description: "Look up a release ticket by id.",
|
|
201
|
+
parameters: {
|
|
202
|
+
type: "object",
|
|
203
|
+
properties: {
|
|
204
|
+
ticket_id: { type: "string" },
|
|
205
|
+
},
|
|
206
|
+
required: ["ticket_id"],
|
|
207
|
+
additionalProperties: false,
|
|
208
|
+
},
|
|
209
|
+
strict: true,
|
|
210
|
+
defer_loading: true,
|
|
211
|
+
},
|
|
212
|
+
{ type: "computer" },
|
|
213
|
+
],
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
await withMockedOpenAI(FakeOpenAI, async () => {
|
|
217
|
+
const modulePath = require.resolve("../src/responses/methods.js");
|
|
218
|
+
delete require.cache[modulePath];
|
|
219
|
+
const responsesMethods = require("../src/responses/methods.js");
|
|
220
|
+
|
|
221
|
+
const clientContext = { clientParams: { apiKey: "sk-test", baseURL: "https://api.example.com/v1" } };
|
|
222
|
+
|
|
223
|
+
const response = await responsesMethods.createModelResponse.call(clientContext, {
|
|
224
|
+
payload: requestPayload,
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
assert.deepEqual(response, { id: "resp_123", status: "completed" });
|
|
228
|
+
|
|
229
|
+
delete require.cache[modulePath];
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
const createCalls = calls.filter((entry) => entry.method === "responses.create");
|
|
233
|
+
assert.deepEqual(createCalls, [
|
|
234
|
+
{
|
|
235
|
+
method: "responses.create",
|
|
236
|
+
payload: requestPayload,
|
|
237
|
+
},
|
|
238
|
+
]);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
test("responses example flows remain valid JSON and cover the documented agentic payload shapes", () => {
|
|
242
|
+
const phaseExamplePath = path.join(
|
|
243
|
+
__dirname,
|
|
244
|
+
"..",
|
|
245
|
+
"examples",
|
|
246
|
+
"responses",
|
|
247
|
+
"phase.json"
|
|
248
|
+
);
|
|
249
|
+
const toolSearchExamplePath = path.join(
|
|
250
|
+
__dirname,
|
|
251
|
+
"..",
|
|
252
|
+
"examples",
|
|
253
|
+
"responses",
|
|
254
|
+
"tool-search.json"
|
|
255
|
+
);
|
|
256
|
+
const computerUseExamplePath = path.join(
|
|
257
|
+
__dirname,
|
|
258
|
+
"..",
|
|
259
|
+
"examples",
|
|
260
|
+
"responses",
|
|
261
|
+
"computer-use.json"
|
|
262
|
+
);
|
|
263
|
+
const websocketExamplePath = path.join(
|
|
264
|
+
__dirname,
|
|
265
|
+
"..",
|
|
266
|
+
"examples",
|
|
267
|
+
"responses",
|
|
268
|
+
"websocket.json"
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
const phaseExample = JSON.parse(fs.readFileSync(phaseExamplePath, "utf8"));
|
|
272
|
+
const toolSearchExample = JSON.parse(
|
|
273
|
+
fs.readFileSync(toolSearchExamplePath, "utf8")
|
|
274
|
+
);
|
|
275
|
+
const computerUseExample = JSON.parse(
|
|
276
|
+
fs.readFileSync(computerUseExamplePath, "utf8")
|
|
277
|
+
);
|
|
278
|
+
const websocketExample = JSON.parse(
|
|
279
|
+
fs.readFileSync(websocketExamplePath, "utf8")
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
[phaseExample, toolSearchExample, computerUseExample].forEach((flow) => {
|
|
283
|
+
assert.ok(Array.isArray(flow));
|
|
284
|
+
const openaiNode = flow.find((entry) => entry.type === "OpenAI API");
|
|
285
|
+
const commentNodes = flow.filter((entry) => entry.type === "comment");
|
|
286
|
+
assert.ok(openaiNode);
|
|
287
|
+
assert.equal(openaiNode.method, "createModelResponse");
|
|
288
|
+
assert.ok(commentNodes.length >= 1);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
const phaseInjectNode = phaseExample.find(
|
|
292
|
+
(entry) => entry.type === "inject" && entry.name === "Create Phased Response"
|
|
293
|
+
);
|
|
294
|
+
const toolSearchInjectNode = toolSearchExample.find(
|
|
295
|
+
(entry) =>
|
|
296
|
+
entry.type === "inject" && entry.name === "Create Tool Search Request"
|
|
297
|
+
);
|
|
298
|
+
const computerCreateInjectNode = computerUseExample.find(
|
|
299
|
+
(entry) => entry.type === "inject" && entry.name === "Create Computer Request"
|
|
300
|
+
);
|
|
301
|
+
const computerFollowupInjectNode = computerUseExample.find(
|
|
302
|
+
(entry) =>
|
|
303
|
+
entry.type === "inject" &&
|
|
304
|
+
entry.name === "Submit Computer Screenshot (edit placeholders)"
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
assert.ok(phaseInjectNode);
|
|
308
|
+
assert.ok(toolSearchInjectNode);
|
|
309
|
+
assert.ok(computerCreateInjectNode);
|
|
310
|
+
assert.ok(computerFollowupInjectNode);
|
|
311
|
+
|
|
312
|
+
const phaseMessage = JSON.parse(
|
|
313
|
+
phaseInjectNode.props.find((prop) => prop.p === "ai.input[0]").v
|
|
314
|
+
);
|
|
315
|
+
const toolSearchTool = JSON.parse(
|
|
316
|
+
toolSearchInjectNode.props.find((prop) => prop.p === "ai.tools[0]").v
|
|
317
|
+
);
|
|
318
|
+
const deferredMcpTool = JSON.parse(
|
|
319
|
+
toolSearchInjectNode.props.find((prop) => prop.p === "ai.tools[1]").v
|
|
320
|
+
);
|
|
321
|
+
const computerTool = JSON.parse(
|
|
322
|
+
computerCreateInjectNode.props.find((prop) => prop.p === "ai.tools[0]").v
|
|
323
|
+
);
|
|
324
|
+
const computerCallOutput = JSON.parse(
|
|
325
|
+
computerFollowupInjectNode.props.find((prop) => prop.p === "ai.input[0]").v
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
assert.equal(
|
|
329
|
+
phaseInjectNode.props.find((prop) => prop.p === "ai.prompt_cache_key").v,
|
|
330
|
+
"responses-phase-example-v1"
|
|
331
|
+
);
|
|
332
|
+
assert.equal(phaseMessage.phase, "commentary");
|
|
333
|
+
assert.equal(toolSearchTool.type, "tool_search");
|
|
334
|
+
assert.equal(deferredMcpTool.defer_loading, true);
|
|
335
|
+
assert.equal(computerTool.type, "computer");
|
|
336
|
+
assert.equal(computerCallOutput.type, "computer_call_output");
|
|
337
|
+
assert.equal(computerCallOutput.output.type, "computer_screenshot");
|
|
338
|
+
|
|
339
|
+
assert.ok(Array.isArray(websocketExample));
|
|
340
|
+
const websocketOpenaiNode = websocketExample.find(
|
|
341
|
+
(entry) => entry.type === "OpenAI API"
|
|
342
|
+
);
|
|
343
|
+
const websocketCommentNodes = websocketExample.filter(
|
|
344
|
+
(entry) => entry.type === "comment"
|
|
345
|
+
);
|
|
346
|
+
const connectInjectNode = websocketExample.find(
|
|
347
|
+
(entry) =>
|
|
348
|
+
entry.type === "inject" && entry.name === "Connect Responses WebSocket"
|
|
349
|
+
);
|
|
350
|
+
const sendInjectNode = websocketExample.find(
|
|
351
|
+
(entry) =>
|
|
352
|
+
entry.type === "inject" && entry.name === "Send response.create Event"
|
|
353
|
+
);
|
|
354
|
+
const closeInjectNode = websocketExample.find(
|
|
355
|
+
(entry) =>
|
|
356
|
+
entry.type === "inject" && entry.name === "Close Responses WebSocket"
|
|
357
|
+
);
|
|
358
|
+
|
|
359
|
+
assert.ok(websocketOpenaiNode);
|
|
360
|
+
assert.equal(websocketOpenaiNode.method, "manageModelResponseWebSocket");
|
|
361
|
+
assert.ok(websocketCommentNodes.length >= 2);
|
|
362
|
+
assert.ok(connectInjectNode);
|
|
363
|
+
assert.ok(sendInjectNode);
|
|
364
|
+
assert.ok(closeInjectNode);
|
|
365
|
+
assert.equal(
|
|
366
|
+
connectInjectNode.props.find((prop) => prop.p === "ai.action").v,
|
|
367
|
+
"connect"
|
|
368
|
+
);
|
|
369
|
+
assert.equal(
|
|
370
|
+
sendInjectNode.props.find((prop) => prop.p === "ai.action").v,
|
|
371
|
+
"send"
|
|
372
|
+
);
|
|
373
|
+
assert.equal(
|
|
374
|
+
closeInjectNode.props.find((prop) => prop.p === "ai.action").v,
|
|
375
|
+
"close"
|
|
376
|
+
);
|
|
377
|
+
assert.deepEqual(
|
|
378
|
+
JSON.parse(sendInjectNode.props.find((prop) => prop.p === "ai.event").v),
|
|
379
|
+
{
|
|
380
|
+
type: "response.create",
|
|
381
|
+
model: "gpt-5.4",
|
|
382
|
+
input: "Say hello from Responses websocket mode in one sentence.",
|
|
383
|
+
}
|
|
384
|
+
);
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
test("responses help documents websocket lifecycle contract", () => {
|
|
388
|
+
const responsesHelpPath = path.join(__dirname, "..", "src", "responses", "help.html");
|
|
389
|
+
const responsesHelp = fs.readFileSync(responsesHelpPath, "utf8");
|
|
390
|
+
|
|
391
|
+
assert.match(responsesHelp, /Manage Model Response WebSocket/);
|
|
392
|
+
assert.match(responsesHelp, /msg\.payload\.action/);
|
|
393
|
+
assert.match(responsesHelp, /connect<\/code>, <code>send<\/code>, or <code>close<\/code>/);
|
|
394
|
+
assert.match(responsesHelp, /msg\.openai/);
|
|
395
|
+
assert.match(responsesHelp, /custom auth headers and query-string auth/);
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
test("realtime example flow remains valid JSON and documents the nested session contract", () => {
|
|
399
|
+
const realtimeExamplePath = path.join(
|
|
400
|
+
__dirname,
|
|
401
|
+
"..",
|
|
402
|
+
"examples",
|
|
403
|
+
"realtime",
|
|
404
|
+
"client-secrets.json"
|
|
405
|
+
);
|
|
406
|
+
|
|
407
|
+
const realtimeExample = JSON.parse(fs.readFileSync(realtimeExamplePath, "utf8"));
|
|
408
|
+
assert.ok(Array.isArray(realtimeExample));
|
|
409
|
+
|
|
410
|
+
const openaiNode = realtimeExample.find((entry) => entry.type === "OpenAI API");
|
|
411
|
+
const commentNodes = realtimeExample.filter((entry) => entry.type === "comment");
|
|
412
|
+
const explainerComment = realtimeExample.find(
|
|
413
|
+
(entry) => entry.type === "comment" && entry.name === "What is a client secret?"
|
|
414
|
+
);
|
|
415
|
+
const realtimeInjectNode = realtimeExample.find(
|
|
416
|
+
(entry) =>
|
|
417
|
+
entry.type === "inject" &&
|
|
418
|
+
entry.name === "Create Realtime 1.5 Client Secret"
|
|
419
|
+
);
|
|
420
|
+
const audioInjectNode = realtimeExample.find(
|
|
421
|
+
(entry) =>
|
|
422
|
+
entry.type === "inject" &&
|
|
423
|
+
entry.name === "Create Audio 1.5 Client Secret"
|
|
424
|
+
);
|
|
425
|
+
|
|
426
|
+
assert.ok(openaiNode);
|
|
427
|
+
assert.equal(openaiNode.method, "createRealtimeClientSecret");
|
|
428
|
+
assert.ok(commentNodes.length >= 3);
|
|
429
|
+
assert.ok(explainerComment);
|
|
430
|
+
assert.match(explainerComment.info, /not your long-lived OpenAI API key/);
|
|
431
|
+
assert.ok(realtimeInjectNode);
|
|
432
|
+
assert.ok(audioInjectNode);
|
|
433
|
+
|
|
434
|
+
assert.equal(
|
|
435
|
+
realtimeInjectNode.props.find((prop) => prop.p === "ai.session.type").v,
|
|
436
|
+
"realtime"
|
|
437
|
+
);
|
|
438
|
+
assert.equal(
|
|
439
|
+
realtimeInjectNode.props.find((prop) => prop.p === "ai.session.model").v,
|
|
440
|
+
"gpt-realtime-1.5"
|
|
441
|
+
);
|
|
442
|
+
assert.equal(
|
|
443
|
+
audioInjectNode.props.find((prop) => prop.p === "ai.session.type").v,
|
|
444
|
+
"realtime"
|
|
445
|
+
);
|
|
446
|
+
assert.equal(
|
|
447
|
+
audioInjectNode.props.find((prop) => prop.p === "ai.session.model").v,
|
|
448
|
+
"gpt-audio-1.5"
|
|
449
|
+
);
|
|
450
|
+
assert.equal(
|
|
451
|
+
realtimeInjectNode.props.find((prop) => prop.p === "ai.expires_after.seconds").v,
|
|
452
|
+
"600"
|
|
453
|
+
);
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
test("responses retrieve streams chunks when stream=true", async () => {
|
|
457
|
+
const calls = [];
|
|
458
|
+
|
|
459
|
+
async function* createFakeStream() {
|
|
460
|
+
yield { type: "response.in_progress", sequence_number: 1 };
|
|
461
|
+
yield { type: "response.completed", sequence_number: 2 };
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
class FakeOpenAI {
|
|
465
|
+
constructor(clientParams) {
|
|
466
|
+
calls.push({ method: "ctor", clientParams });
|
|
467
|
+
this.responses = {
|
|
468
|
+
retrieve: async (responseId, options) => {
|
|
469
|
+
calls.push({ method: "responses.retrieve", responseId, options });
|
|
470
|
+
if (options.stream) {
|
|
471
|
+
return createFakeStream();
|
|
472
|
+
}
|
|
473
|
+
return { id: responseId, status: "completed" };
|
|
474
|
+
},
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
await withMockedOpenAI(FakeOpenAI, async () => {
|
|
480
|
+
const modulePath = require.resolve("../src/responses/methods.js");
|
|
481
|
+
delete require.cache[modulePath];
|
|
482
|
+
const responsesMethods = require("../src/responses/methods.js");
|
|
483
|
+
|
|
484
|
+
const sentMessages = [];
|
|
485
|
+
const statuses = [];
|
|
486
|
+
const node = {
|
|
487
|
+
send: (msg) => sentMessages.push(msg),
|
|
488
|
+
status: (status) => statuses.push(status),
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
const clientContext = { clientParams: { apiKey: "sk-test" } };
|
|
492
|
+
|
|
493
|
+
const streamResult = await responsesMethods.getModelResponse.call(clientContext, {
|
|
494
|
+
_node: node,
|
|
495
|
+
msg: { topic: "t1" },
|
|
496
|
+
payload: {
|
|
497
|
+
response_id: "resp_stream",
|
|
498
|
+
stream: true,
|
|
499
|
+
},
|
|
500
|
+
});
|
|
501
|
+
assert.equal(streamResult, undefined);
|
|
502
|
+
|
|
503
|
+
assert.deepEqual(sentMessages, [
|
|
504
|
+
{
|
|
505
|
+
topic: "t1",
|
|
506
|
+
payload: { type: "response.in_progress", sequence_number: 1 },
|
|
507
|
+
},
|
|
508
|
+
{
|
|
509
|
+
topic: "t1",
|
|
510
|
+
payload: { type: "response.completed", sequence_number: 2 },
|
|
511
|
+
},
|
|
512
|
+
]);
|
|
513
|
+
|
|
514
|
+
assert.deepEqual(statuses, [
|
|
515
|
+
{
|
|
516
|
+
fill: "green",
|
|
517
|
+
shape: "dot",
|
|
518
|
+
text: "OpenaiApi.status.streaming",
|
|
519
|
+
},
|
|
520
|
+
{},
|
|
521
|
+
]);
|
|
522
|
+
|
|
523
|
+
const retrieveResponse = await responsesMethods.getModelResponse.call(clientContext, {
|
|
524
|
+
_node: node,
|
|
525
|
+
msg: { topic: "t1" },
|
|
526
|
+
payload: {
|
|
527
|
+
response_id: "resp_non_stream",
|
|
528
|
+
},
|
|
529
|
+
});
|
|
530
|
+
assert.deepEqual(retrieveResponse, { id: "resp_non_stream", status: "completed" });
|
|
531
|
+
|
|
532
|
+
delete require.cache[modulePath];
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
const retrieveCalls = calls.filter((entry) => entry.method === "responses.retrieve");
|
|
536
|
+
assert.deepEqual(retrieveCalls, [
|
|
537
|
+
{
|
|
538
|
+
method: "responses.retrieve",
|
|
539
|
+
responseId: "resp_stream",
|
|
540
|
+
options: { stream: true },
|
|
541
|
+
},
|
|
542
|
+
{
|
|
543
|
+
method: "responses.retrieve",
|
|
544
|
+
responseId: "resp_non_stream",
|
|
545
|
+
options: {},
|
|
546
|
+
},
|
|
547
|
+
]);
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
test("conversation methods map to OpenAI SDK conversations endpoints", async () => {
|
|
551
|
+
const calls = [];
|
|
552
|
+
|
|
553
|
+
class FakeOpenAI {
|
|
554
|
+
constructor(clientParams) {
|
|
555
|
+
calls.push({ method: "ctor", clientParams });
|
|
556
|
+
|
|
557
|
+
this.conversations = {
|
|
558
|
+
create: async (body) => {
|
|
559
|
+
calls.push({ method: "conversations.create", body });
|
|
560
|
+
return { id: "conv_new" };
|
|
561
|
+
},
|
|
562
|
+
retrieve: async (conversationId, options) => {
|
|
563
|
+
calls.push({ method: "conversations.retrieve", conversationId, options });
|
|
564
|
+
return { id: conversationId };
|
|
565
|
+
},
|
|
566
|
+
update: async (conversationId, body) => {
|
|
567
|
+
calls.push({ method: "conversations.update", conversationId, body });
|
|
568
|
+
return { id: conversationId, updated: true };
|
|
569
|
+
},
|
|
570
|
+
delete: async (conversationId, options) => {
|
|
571
|
+
calls.push({ method: "conversations.delete", conversationId, options });
|
|
572
|
+
return { id: conversationId, deleted: true };
|
|
573
|
+
},
|
|
574
|
+
items: {
|
|
575
|
+
create: async (conversationId, body) => {
|
|
576
|
+
calls.push({ method: "conversations.items.create", conversationId, body });
|
|
577
|
+
return { id: "item_new" };
|
|
578
|
+
},
|
|
579
|
+
retrieve: async (itemId, options) => {
|
|
580
|
+
calls.push({ method: "conversations.items.retrieve", itemId, options });
|
|
581
|
+
return { id: itemId };
|
|
582
|
+
},
|
|
583
|
+
list: async (conversationId, options) => {
|
|
584
|
+
calls.push({ method: "conversations.items.list", conversationId, options });
|
|
585
|
+
return { data: [{ id: "item_1" }, { id: "item_2" }] };
|
|
586
|
+
},
|
|
587
|
+
delete: async (itemId, options) => {
|
|
588
|
+
calls.push({ method: "conversations.items.delete", itemId, options });
|
|
589
|
+
return { id: itemId, deleted: true };
|
|
590
|
+
},
|
|
591
|
+
},
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
await withMockedOpenAI(FakeOpenAI, async () => {
|
|
597
|
+
const modulePath = require.resolve("../src/conversations/methods.js");
|
|
598
|
+
delete require.cache[modulePath];
|
|
599
|
+
const conversationMethods = require("../src/conversations/methods.js");
|
|
600
|
+
|
|
601
|
+
const clientContext = { clientParams: { apiKey: "sk-test" } };
|
|
602
|
+
|
|
603
|
+
const created = await conversationMethods.createConversation.call(clientContext, {
|
|
604
|
+
payload: { metadata: { app: "node-red" } },
|
|
605
|
+
});
|
|
606
|
+
assert.deepEqual(created, { id: "conv_new" });
|
|
607
|
+
|
|
608
|
+
const fetched = await conversationMethods.getConversation.call(clientContext, {
|
|
609
|
+
payload: { conversation_id: "conv_1", include: ["items"] },
|
|
610
|
+
});
|
|
611
|
+
assert.deepEqual(fetched, { id: "conv_1" });
|
|
612
|
+
|
|
613
|
+
const updated = await conversationMethods.modifyConversation.call(clientContext, {
|
|
614
|
+
payload: { conversation_id: "conv_1", metadata: { env: "dev" } },
|
|
615
|
+
});
|
|
616
|
+
assert.deepEqual(updated, { id: "conv_1", updated: true });
|
|
617
|
+
|
|
618
|
+
const deleted = await conversationMethods.deleteConversation.call(clientContext, {
|
|
619
|
+
payload: { conversation_id: "conv_1" },
|
|
620
|
+
});
|
|
621
|
+
assert.deepEqual(deleted, { id: "conv_1", deleted: true });
|
|
622
|
+
|
|
623
|
+
const createdItem = await conversationMethods.createConversationItem.call(clientContext, {
|
|
624
|
+
payload: {
|
|
625
|
+
conversation_id: "conv_1",
|
|
626
|
+
item: { role: "user", content: [{ type: "text", text: "hello" }] },
|
|
627
|
+
},
|
|
628
|
+
});
|
|
629
|
+
assert.deepEqual(createdItem, { id: "item_new" });
|
|
630
|
+
|
|
631
|
+
const fetchedItem = await conversationMethods.getConversationItem.call(clientContext, {
|
|
632
|
+
payload: {
|
|
633
|
+
conversation_id: "conv_1",
|
|
634
|
+
item_id: "item_1",
|
|
635
|
+
},
|
|
636
|
+
});
|
|
637
|
+
assert.deepEqual(fetchedItem, { id: "item_1" });
|
|
638
|
+
|
|
639
|
+
const listedItems = await conversationMethods.listConversationItems.call(clientContext, {
|
|
640
|
+
payload: {
|
|
641
|
+
conversation_id: "conv_1",
|
|
642
|
+
limit: 2,
|
|
643
|
+
},
|
|
644
|
+
});
|
|
645
|
+
assert.deepEqual(listedItems, [{ id: "item_1" }, { id: "item_2" }]);
|
|
646
|
+
|
|
647
|
+
const deletedItem = await conversationMethods.deleteConversationItem.call(clientContext, {
|
|
648
|
+
payload: {
|
|
649
|
+
conversation_id: "conv_1",
|
|
650
|
+
item_id: "item_1",
|
|
651
|
+
},
|
|
652
|
+
});
|
|
653
|
+
assert.deepEqual(deletedItem, { id: "item_1", deleted: true });
|
|
654
|
+
|
|
655
|
+
delete require.cache[modulePath];
|
|
656
|
+
});
|
|
657
|
+
|
|
658
|
+
assert.equal(calls.some((entry) => entry.method === "conversations.create"), true);
|
|
659
|
+
assert.equal(calls.some((entry) => entry.method === "conversations.items.list"), true);
|
|
660
|
+
assert.equal(calls.some((entry) => entry.method === "conversations.items.delete"), true);
|
|
661
|
+
});
|
|
662
|
+
|
|
663
|
+
test("skills methods map to OpenAI SDK skills endpoints", async () => {
|
|
664
|
+
const calls = [];
|
|
665
|
+
|
|
666
|
+
class FakeOpenAI {
|
|
667
|
+
constructor(clientParams) {
|
|
668
|
+
calls.push({ method: "ctor", clientParams });
|
|
669
|
+
this.skills = {
|
|
670
|
+
create: async (body) => {
|
|
671
|
+
calls.push({ method: "skills.create", body });
|
|
672
|
+
return { id: "skill_new" };
|
|
673
|
+
},
|
|
674
|
+
retrieve: async (skillId, options) => {
|
|
675
|
+
calls.push({ method: "skills.retrieve", skillId, options });
|
|
676
|
+
return { id: skillId };
|
|
677
|
+
},
|
|
678
|
+
update: async (skillId, body) => {
|
|
679
|
+
calls.push({ method: "skills.update", skillId, body });
|
|
680
|
+
return { id: skillId, updated: true };
|
|
681
|
+
},
|
|
682
|
+
delete: async (skillId, options) => {
|
|
683
|
+
calls.push({ method: "skills.delete", skillId, options });
|
|
684
|
+
return { id: skillId, deleted: true };
|
|
685
|
+
},
|
|
686
|
+
list: async (options) => {
|
|
687
|
+
calls.push({ method: "skills.list", options });
|
|
688
|
+
return { data: [{ id: "skill_1" }, { id: "skill_2" }] };
|
|
689
|
+
},
|
|
690
|
+
content: {
|
|
691
|
+
retrieve: async (skillId, options) => {
|
|
692
|
+
calls.push({ method: "skills.content.retrieve", skillId, options });
|
|
693
|
+
return { id: skillId, object: "skill.content" };
|
|
694
|
+
},
|
|
695
|
+
},
|
|
696
|
+
versions: {
|
|
697
|
+
create: async (skillId, body) => {
|
|
698
|
+
calls.push({ method: "skills.versions.create", skillId, body });
|
|
699
|
+
return { id: "skill_version_new", skill_id: skillId };
|
|
700
|
+
},
|
|
701
|
+
retrieve: async (version, options) => {
|
|
702
|
+
calls.push({ method: "skills.versions.retrieve", version, options });
|
|
703
|
+
return { id: "skill_version_1", version };
|
|
704
|
+
},
|
|
705
|
+
list: async (skillId, options) => {
|
|
706
|
+
calls.push({ method: "skills.versions.list", skillId, options });
|
|
707
|
+
return { data: [{ id: "sv_1" }, { id: "sv_2" }] };
|
|
708
|
+
},
|
|
709
|
+
delete: async (version, options) => {
|
|
710
|
+
calls.push({ method: "skills.versions.delete", version, options });
|
|
711
|
+
return { deleted: true, version };
|
|
712
|
+
},
|
|
713
|
+
content: {
|
|
714
|
+
retrieve: async (version, options) => {
|
|
715
|
+
calls.push({ method: "skills.versions.content.retrieve", version, options });
|
|
716
|
+
return { object: "skill.version.content", version };
|
|
717
|
+
},
|
|
718
|
+
},
|
|
719
|
+
},
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
await withMockedOpenAI(FakeOpenAI, async () => {
|
|
725
|
+
const modulePath = require.resolve("../src/skills/methods.js");
|
|
726
|
+
delete require.cache[modulePath];
|
|
727
|
+
const skillMethods = require("../src/skills/methods.js");
|
|
728
|
+
|
|
729
|
+
const clientContext = { clientParams: { apiKey: "sk-test" } };
|
|
730
|
+
|
|
731
|
+
const listed = await skillMethods.listSkills.call(clientContext, {
|
|
732
|
+
payload: { order: "desc", limit: 2 },
|
|
733
|
+
});
|
|
734
|
+
assert.deepEqual(listed, [{ id: "skill_1" }, { id: "skill_2" }]);
|
|
735
|
+
|
|
736
|
+
const created = await skillMethods.createSkill.call(clientContext, {
|
|
737
|
+
payload: { files: ["./skill.zip"] },
|
|
738
|
+
});
|
|
739
|
+
assert.deepEqual(created, { id: "skill_new" });
|
|
740
|
+
|
|
741
|
+
const fetched = await skillMethods.getSkill.call(clientContext, {
|
|
742
|
+
payload: { skill_id: "skill_1" },
|
|
743
|
+
});
|
|
744
|
+
assert.deepEqual(fetched, { id: "skill_1" });
|
|
745
|
+
|
|
746
|
+
const updated = await skillMethods.modifySkill.call(clientContext, {
|
|
747
|
+
payload: { skill_id: "skill_1", default_version: "2" },
|
|
748
|
+
});
|
|
749
|
+
assert.deepEqual(updated, { id: "skill_1", updated: true });
|
|
750
|
+
|
|
751
|
+
const deleted = await skillMethods.deleteSkill.call(clientContext, {
|
|
752
|
+
payload: { skill_id: "skill_1" },
|
|
753
|
+
});
|
|
754
|
+
assert.deepEqual(deleted, { id: "skill_1", deleted: true });
|
|
755
|
+
|
|
756
|
+
const content = await skillMethods.getSkillContent.call(clientContext, {
|
|
757
|
+
payload: { skill_id: "skill_1" },
|
|
758
|
+
});
|
|
759
|
+
assert.deepEqual(content, { id: "skill_1", object: "skill.content" });
|
|
760
|
+
|
|
761
|
+
const listedVersions = await skillMethods.listSkillVersions.call(clientContext, {
|
|
762
|
+
payload: { skill_id: "skill_1", order: "asc" },
|
|
763
|
+
});
|
|
764
|
+
assert.deepEqual(listedVersions, [{ id: "sv_1" }, { id: "sv_2" }]);
|
|
765
|
+
|
|
766
|
+
const createdVersion = await skillMethods.createSkillVersion.call(clientContext, {
|
|
767
|
+
payload: { skill_id: "skill_1", default: true, files: ["./v2.zip"] },
|
|
768
|
+
});
|
|
769
|
+
assert.deepEqual(createdVersion, { id: "skill_version_new", skill_id: "skill_1" });
|
|
770
|
+
|
|
771
|
+
const fetchedVersion = await skillMethods.getSkillVersion.call(clientContext, {
|
|
772
|
+
payload: { skill_id: "skill_1", version: "2" },
|
|
773
|
+
});
|
|
774
|
+
assert.deepEqual(fetchedVersion, { id: "skill_version_1", version: "2" });
|
|
775
|
+
|
|
776
|
+
const deletedVersion = await skillMethods.deleteSkillVersion.call(clientContext, {
|
|
777
|
+
payload: { skill_id: "skill_1", version: "2" },
|
|
778
|
+
});
|
|
779
|
+
assert.deepEqual(deletedVersion, { deleted: true, version: "2" });
|
|
780
|
+
|
|
781
|
+
const versionContent = await skillMethods.getSkillVersionContent.call(clientContext, {
|
|
782
|
+
payload: { skill_id: "skill_1", version: "2" },
|
|
783
|
+
});
|
|
784
|
+
assert.deepEqual(versionContent, { object: "skill.version.content", version: "2" });
|
|
785
|
+
|
|
786
|
+
delete require.cache[modulePath];
|
|
787
|
+
});
|
|
788
|
+
|
|
789
|
+
const skillCalls = calls.filter((entry) => entry.method !== "ctor");
|
|
790
|
+
assert.deepEqual(skillCalls, [
|
|
791
|
+
{
|
|
792
|
+
method: "skills.list",
|
|
793
|
+
options: { order: "desc", limit: 2 },
|
|
794
|
+
},
|
|
795
|
+
{
|
|
796
|
+
method: "skills.create",
|
|
797
|
+
body: { files: ["./skill.zip"] },
|
|
798
|
+
},
|
|
799
|
+
{
|
|
800
|
+
method: "skills.retrieve",
|
|
801
|
+
skillId: "skill_1",
|
|
802
|
+
options: {},
|
|
803
|
+
},
|
|
804
|
+
{
|
|
805
|
+
method: "skills.update",
|
|
806
|
+
skillId: "skill_1",
|
|
807
|
+
body: { default_version: "2" },
|
|
808
|
+
},
|
|
809
|
+
{
|
|
810
|
+
method: "skills.delete",
|
|
811
|
+
skillId: "skill_1",
|
|
812
|
+
options: {},
|
|
813
|
+
},
|
|
814
|
+
{
|
|
815
|
+
method: "skills.content.retrieve",
|
|
816
|
+
skillId: "skill_1",
|
|
817
|
+
options: {},
|
|
818
|
+
},
|
|
819
|
+
{
|
|
820
|
+
method: "skills.versions.list",
|
|
821
|
+
skillId: "skill_1",
|
|
822
|
+
options: { order: "asc" },
|
|
823
|
+
},
|
|
824
|
+
{
|
|
825
|
+
method: "skills.versions.create",
|
|
826
|
+
skillId: "skill_1",
|
|
827
|
+
body: { default: true, files: ["./v2.zip"] },
|
|
828
|
+
},
|
|
829
|
+
{
|
|
830
|
+
method: "skills.versions.retrieve",
|
|
831
|
+
version: "2",
|
|
832
|
+
options: { skill_id: "skill_1" },
|
|
833
|
+
},
|
|
834
|
+
{
|
|
835
|
+
method: "skills.versions.delete",
|
|
836
|
+
version: "2",
|
|
837
|
+
options: { skill_id: "skill_1" },
|
|
838
|
+
},
|
|
839
|
+
{
|
|
840
|
+
method: "skills.versions.content.retrieve",
|
|
841
|
+
version: "2",
|
|
842
|
+
options: { skill_id: "skill_1" },
|
|
843
|
+
},
|
|
844
|
+
]);
|
|
845
|
+
});
|
|
846
|
+
|
|
847
|
+
test("evals methods map to OpenAI SDK evals endpoints", async () => {
|
|
848
|
+
const calls = [];
|
|
849
|
+
|
|
850
|
+
class FakeOpenAI {
|
|
851
|
+
constructor(clientParams) {
|
|
852
|
+
calls.push({ method: "ctor", clientParams });
|
|
853
|
+
this.evals = {
|
|
854
|
+
create: async (body) => {
|
|
855
|
+
calls.push({ method: "evals.create", body });
|
|
856
|
+
return { id: "eval_new" };
|
|
857
|
+
},
|
|
858
|
+
retrieve: async (evalId, options) => {
|
|
859
|
+
calls.push({ method: "evals.retrieve", evalId, options });
|
|
860
|
+
return { id: evalId };
|
|
861
|
+
},
|
|
862
|
+
update: async (evalId, body) => {
|
|
863
|
+
calls.push({ method: "evals.update", evalId, body });
|
|
864
|
+
return { id: evalId, updated: true };
|
|
865
|
+
},
|
|
866
|
+
delete: async (evalId, options) => {
|
|
867
|
+
calls.push({ method: "evals.delete", evalId, options });
|
|
868
|
+
return { id: evalId, deleted: true };
|
|
869
|
+
},
|
|
870
|
+
list: async (options) => {
|
|
871
|
+
calls.push({ method: "evals.list", options });
|
|
872
|
+
return { data: [{ id: "eval_1" }, { id: "eval_2" }] };
|
|
873
|
+
},
|
|
874
|
+
runs: {
|
|
875
|
+
create: async (evalId, body) => {
|
|
876
|
+
calls.push({ method: "evals.runs.create", evalId, body });
|
|
877
|
+
return { id: "run_new", eval_id: evalId };
|
|
878
|
+
},
|
|
879
|
+
retrieve: async (runId, options) => {
|
|
880
|
+
calls.push({ method: "evals.runs.retrieve", runId, options });
|
|
881
|
+
return { id: runId };
|
|
882
|
+
},
|
|
883
|
+
list: async (evalId, options) => {
|
|
884
|
+
calls.push({ method: "evals.runs.list", evalId, options });
|
|
885
|
+
return { data: [{ id: "run_1" }, { id: "run_2" }] };
|
|
886
|
+
},
|
|
887
|
+
delete: async (runId, options) => {
|
|
888
|
+
calls.push({ method: "evals.runs.delete", runId, options });
|
|
889
|
+
return { id: runId, deleted: true };
|
|
890
|
+
},
|
|
891
|
+
cancel: async (runId, options) => {
|
|
892
|
+
calls.push({ method: "evals.runs.cancel", runId, options });
|
|
893
|
+
return { id: runId, status: "cancelled" };
|
|
894
|
+
},
|
|
895
|
+
outputItems: {
|
|
896
|
+
retrieve: async (outputItemId, options) => {
|
|
897
|
+
calls.push({
|
|
898
|
+
method: "evals.runs.outputItems.retrieve",
|
|
899
|
+
outputItemId,
|
|
900
|
+
options,
|
|
901
|
+
});
|
|
902
|
+
return { id: outputItemId };
|
|
903
|
+
},
|
|
904
|
+
list: async (runId, options) => {
|
|
905
|
+
calls.push({ method: "evals.runs.outputItems.list", runId, options });
|
|
906
|
+
return { data: [{ id: "out_1" }, { id: "out_2" }] };
|
|
907
|
+
},
|
|
908
|
+
},
|
|
909
|
+
},
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
await withMockedOpenAI(FakeOpenAI, async () => {
|
|
915
|
+
const modulePath = require.resolve("../src/evals/methods.js");
|
|
916
|
+
delete require.cache[modulePath];
|
|
917
|
+
const evalMethods = require("../src/evals/methods.js");
|
|
918
|
+
|
|
919
|
+
const clientContext = { clientParams: { apiKey: "sk-test" } };
|
|
920
|
+
|
|
921
|
+
const listedEvals = await evalMethods.listEvals.call(clientContext, {
|
|
922
|
+
payload: { order: "desc", limit: 2 },
|
|
923
|
+
});
|
|
924
|
+
assert.deepEqual(listedEvals, [{ id: "eval_1" }, { id: "eval_2" }]);
|
|
925
|
+
|
|
926
|
+
const createdEval = await evalMethods.createEval.call(clientContext, {
|
|
927
|
+
payload: {
|
|
928
|
+
name: "Support quality eval",
|
|
929
|
+
data_source_config: { type: "custom", schema: { type: "object" } },
|
|
930
|
+
testing_criteria: [],
|
|
931
|
+
},
|
|
932
|
+
});
|
|
933
|
+
assert.deepEqual(createdEval, { id: "eval_new" });
|
|
934
|
+
|
|
935
|
+
const fetchedEval = await evalMethods.getEval.call(clientContext, {
|
|
936
|
+
payload: { eval_id: "eval_1" },
|
|
937
|
+
});
|
|
938
|
+
assert.deepEqual(fetchedEval, { id: "eval_1" });
|
|
939
|
+
|
|
940
|
+
const updatedEval = await evalMethods.modifyEval.call(clientContext, {
|
|
941
|
+
payload: { eval_id: "eval_1", name: "Updated eval" },
|
|
942
|
+
});
|
|
943
|
+
assert.deepEqual(updatedEval, { id: "eval_1", updated: true });
|
|
944
|
+
|
|
945
|
+
const deletedEval = await evalMethods.deleteEval.call(clientContext, {
|
|
946
|
+
payload: { eval_id: "eval_1" },
|
|
947
|
+
});
|
|
948
|
+
assert.deepEqual(deletedEval, { id: "eval_1", deleted: true });
|
|
949
|
+
|
|
950
|
+
const listedRuns = await evalMethods.listEvalRuns.call(clientContext, {
|
|
951
|
+
payload: { eval_id: "eval_1", limit: 2 },
|
|
952
|
+
});
|
|
953
|
+
assert.deepEqual(listedRuns, [{ id: "run_1" }, { id: "run_2" }]);
|
|
954
|
+
|
|
955
|
+
const createdRun = await evalMethods.createEvalRun.call(clientContext, {
|
|
956
|
+
payload: {
|
|
957
|
+
eval_id: "eval_1",
|
|
958
|
+
data_source: {
|
|
959
|
+
type: "jsonl",
|
|
960
|
+
source: { type: "file_id", id: "file_1" },
|
|
961
|
+
},
|
|
962
|
+
},
|
|
963
|
+
});
|
|
964
|
+
assert.deepEqual(createdRun, { id: "run_new", eval_id: "eval_1" });
|
|
965
|
+
|
|
966
|
+
const fetchedRun = await evalMethods.getEvalRun.call(clientContext, {
|
|
967
|
+
payload: { eval_id: "eval_1", run_id: "run_1" },
|
|
968
|
+
});
|
|
969
|
+
assert.deepEqual(fetchedRun, { id: "run_1" });
|
|
970
|
+
|
|
971
|
+
const cancelledRun = await evalMethods.cancelEvalRun.call(clientContext, {
|
|
972
|
+
payload: { eval_id: "eval_1", run_id: "run_1" },
|
|
973
|
+
});
|
|
974
|
+
assert.deepEqual(cancelledRun, { id: "run_1", status: "cancelled" });
|
|
975
|
+
|
|
976
|
+
const deletedRun = await evalMethods.deleteEvalRun.call(clientContext, {
|
|
977
|
+
payload: { eval_id: "eval_1", run_id: "run_1" },
|
|
978
|
+
});
|
|
979
|
+
assert.deepEqual(deletedRun, { id: "run_1", deleted: true });
|
|
980
|
+
|
|
981
|
+
const listedOutputItems = await evalMethods.listEvalRunOutputItems.call(clientContext, {
|
|
982
|
+
payload: { eval_id: "eval_1", run_id: "run_1", limit: 1 },
|
|
983
|
+
});
|
|
984
|
+
assert.deepEqual(listedOutputItems, [{ id: "out_1" }, { id: "out_2" }]);
|
|
985
|
+
|
|
986
|
+
const fetchedOutputItem = await evalMethods.getEvalRunOutputItem.call(clientContext, {
|
|
987
|
+
payload: { eval_id: "eval_1", run_id: "run_1", output_item_id: "out_1" },
|
|
988
|
+
});
|
|
989
|
+
assert.deepEqual(fetchedOutputItem, { id: "out_1" });
|
|
990
|
+
|
|
991
|
+
delete require.cache[modulePath];
|
|
992
|
+
});
|
|
993
|
+
|
|
994
|
+
const evalCalls = calls.filter((entry) => entry.method !== "ctor");
|
|
995
|
+
assert.deepEqual(evalCalls, [
|
|
996
|
+
{
|
|
997
|
+
method: "evals.list",
|
|
998
|
+
options: { order: "desc", limit: 2 },
|
|
999
|
+
},
|
|
1000
|
+
{
|
|
1001
|
+
method: "evals.create",
|
|
1002
|
+
body: {
|
|
1003
|
+
name: "Support quality eval",
|
|
1004
|
+
data_source_config: { type: "custom", schema: { type: "object" } },
|
|
1005
|
+
testing_criteria: [],
|
|
1006
|
+
},
|
|
1007
|
+
},
|
|
1008
|
+
{
|
|
1009
|
+
method: "evals.retrieve",
|
|
1010
|
+
evalId: "eval_1",
|
|
1011
|
+
options: {},
|
|
1012
|
+
},
|
|
1013
|
+
{
|
|
1014
|
+
method: "evals.update",
|
|
1015
|
+
evalId: "eval_1",
|
|
1016
|
+
body: { name: "Updated eval" },
|
|
1017
|
+
},
|
|
1018
|
+
{
|
|
1019
|
+
method: "evals.delete",
|
|
1020
|
+
evalId: "eval_1",
|
|
1021
|
+
options: {},
|
|
1022
|
+
},
|
|
1023
|
+
{
|
|
1024
|
+
method: "evals.runs.list",
|
|
1025
|
+
evalId: "eval_1",
|
|
1026
|
+
options: { limit: 2 },
|
|
1027
|
+
},
|
|
1028
|
+
{
|
|
1029
|
+
method: "evals.runs.create",
|
|
1030
|
+
evalId: "eval_1",
|
|
1031
|
+
body: {
|
|
1032
|
+
data_source: {
|
|
1033
|
+
type: "jsonl",
|
|
1034
|
+
source: { type: "file_id", id: "file_1" },
|
|
1035
|
+
},
|
|
1036
|
+
},
|
|
1037
|
+
},
|
|
1038
|
+
{
|
|
1039
|
+
method: "evals.runs.retrieve",
|
|
1040
|
+
runId: "run_1",
|
|
1041
|
+
options: { eval_id: "eval_1" },
|
|
1042
|
+
},
|
|
1043
|
+
{
|
|
1044
|
+
method: "evals.runs.cancel",
|
|
1045
|
+
runId: "run_1",
|
|
1046
|
+
options: { eval_id: "eval_1" },
|
|
1047
|
+
},
|
|
1048
|
+
{
|
|
1049
|
+
method: "evals.runs.delete",
|
|
1050
|
+
runId: "run_1",
|
|
1051
|
+
options: { eval_id: "eval_1" },
|
|
1052
|
+
},
|
|
1053
|
+
{
|
|
1054
|
+
method: "evals.runs.outputItems.list",
|
|
1055
|
+
runId: "run_1",
|
|
1056
|
+
options: { eval_id: "eval_1", limit: 1 },
|
|
1057
|
+
},
|
|
1058
|
+
{
|
|
1059
|
+
method: "evals.runs.outputItems.retrieve",
|
|
1060
|
+
outputItemId: "out_1",
|
|
1061
|
+
options: { eval_id: "eval_1", run_id: "run_1" },
|
|
1062
|
+
},
|
|
1063
|
+
]);
|
|
1064
|
+
});
|
|
1065
|
+
|
|
1066
|
+
test("videos methods map to OpenAI SDK videos endpoints", async () => {
|
|
1067
|
+
const calls = [];
|
|
1068
|
+
|
|
1069
|
+
class FakeOpenAI {
|
|
1070
|
+
constructor(clientParams) {
|
|
1071
|
+
calls.push({ method: "ctor", clientParams });
|
|
1072
|
+
this.videos = {
|
|
1073
|
+
create: async (body) => {
|
|
1074
|
+
calls.push({ method: "videos.create", body });
|
|
1075
|
+
return { id: "video_new" };
|
|
1076
|
+
},
|
|
1077
|
+
retrieve: async (videoId, options) => {
|
|
1078
|
+
calls.push({ method: "videos.retrieve", videoId, options });
|
|
1079
|
+
return { id: videoId };
|
|
1080
|
+
},
|
|
1081
|
+
list: async (options) => {
|
|
1082
|
+
calls.push({ method: "videos.list", options });
|
|
1083
|
+
return { data: [{ id: "video_1" }, { id: "video_2" }] };
|
|
1084
|
+
},
|
|
1085
|
+
delete: async (videoId, options) => {
|
|
1086
|
+
calls.push({ method: "videos.delete", videoId, options });
|
|
1087
|
+
return { id: videoId, deleted: true };
|
|
1088
|
+
},
|
|
1089
|
+
downloadContent: async (videoId, query) => {
|
|
1090
|
+
calls.push({ method: "videos.downloadContent", videoId, query });
|
|
1091
|
+
return { binary: true };
|
|
1092
|
+
},
|
|
1093
|
+
remix: async (videoId, body) => {
|
|
1094
|
+
calls.push({ method: "videos.remix", videoId, body });
|
|
1095
|
+
return { id: "video_remix" };
|
|
1096
|
+
},
|
|
1097
|
+
};
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
await withMockedOpenAI(FakeOpenAI, async () => {
|
|
1102
|
+
const modulePath = require.resolve("../src/videos/methods.js");
|
|
1103
|
+
delete require.cache[modulePath];
|
|
1104
|
+
const videoMethods = require("../src/videos/methods.js");
|
|
1105
|
+
|
|
1106
|
+
const clientContext = { clientParams: { apiKey: "sk-test" } };
|
|
1107
|
+
|
|
1108
|
+
const listedVideos = await videoMethods.listVideos.call(clientContext, {
|
|
1109
|
+
payload: { order: "desc", limit: 1 },
|
|
1110
|
+
});
|
|
1111
|
+
assert.deepEqual(listedVideos, [{ id: "video_1" }, { id: "video_2" }]);
|
|
1112
|
+
|
|
1113
|
+
const createdVideo = await videoMethods.createVideo.call(clientContext, {
|
|
1114
|
+
payload: { prompt: "A sunrise over mountains", model: "sora-2" },
|
|
1115
|
+
});
|
|
1116
|
+
assert.deepEqual(createdVideo, { id: "video_new" });
|
|
1117
|
+
|
|
1118
|
+
const fetchedVideo = await videoMethods.getVideo.call(clientContext, {
|
|
1119
|
+
payload: { video_id: "video_1" },
|
|
1120
|
+
});
|
|
1121
|
+
assert.deepEqual(fetchedVideo, { id: "video_1" });
|
|
1122
|
+
|
|
1123
|
+
const deletedVideo = await videoMethods.deleteVideo.call(clientContext, {
|
|
1124
|
+
payload: { video_id: "video_1" },
|
|
1125
|
+
});
|
|
1126
|
+
assert.deepEqual(deletedVideo, { id: "video_1", deleted: true });
|
|
1127
|
+
|
|
1128
|
+
const downloaded = await videoMethods.downloadVideoContent.call(clientContext, {
|
|
1129
|
+
payload: { video_id: "video_1", variant: "thumbnail" },
|
|
1130
|
+
});
|
|
1131
|
+
assert.deepEqual(downloaded, { binary: true });
|
|
1132
|
+
|
|
1133
|
+
const remixed = await videoMethods.remixVideo.call(clientContext, {
|
|
1134
|
+
payload: { video_id: "video_1", prompt: "Make it cinematic" },
|
|
1135
|
+
});
|
|
1136
|
+
assert.deepEqual(remixed, { id: "video_remix" });
|
|
1137
|
+
|
|
1138
|
+
delete require.cache[modulePath];
|
|
1139
|
+
});
|
|
1140
|
+
|
|
1141
|
+
const videoCalls = calls.filter((entry) => entry.method !== "ctor");
|
|
1142
|
+
assert.deepEqual(videoCalls, [
|
|
1143
|
+
{
|
|
1144
|
+
method: "videos.list",
|
|
1145
|
+
options: { order: "desc", limit: 1 },
|
|
1146
|
+
},
|
|
1147
|
+
{
|
|
1148
|
+
method: "videos.create",
|
|
1149
|
+
body: { prompt: "A sunrise over mountains", model: "sora-2" },
|
|
1150
|
+
},
|
|
1151
|
+
{
|
|
1152
|
+
method: "videos.retrieve",
|
|
1153
|
+
videoId: "video_1",
|
|
1154
|
+
options: {},
|
|
1155
|
+
},
|
|
1156
|
+
{
|
|
1157
|
+
method: "videos.delete",
|
|
1158
|
+
videoId: "video_1",
|
|
1159
|
+
options: {},
|
|
1160
|
+
},
|
|
1161
|
+
{
|
|
1162
|
+
method: "videos.downloadContent",
|
|
1163
|
+
videoId: "video_1",
|
|
1164
|
+
query: { variant: "thumbnail" },
|
|
1165
|
+
},
|
|
1166
|
+
{
|
|
1167
|
+
method: "videos.remix",
|
|
1168
|
+
videoId: "video_1",
|
|
1169
|
+
body: { prompt: "Make it cinematic" },
|
|
1170
|
+
},
|
|
1171
|
+
]);
|
|
1172
|
+
});
|
|
1173
|
+
|
|
1174
|
+
test("realtime methods map to OpenAI SDK realtime endpoints and pass newer model ids through unchanged", async () => {
|
|
1175
|
+
const calls = [];
|
|
1176
|
+
|
|
1177
|
+
class FakeOpenAI {
|
|
1178
|
+
constructor(clientParams) {
|
|
1179
|
+
calls.push({ method: "ctor", clientParams });
|
|
1180
|
+
this.realtime = {
|
|
1181
|
+
clientSecrets: {
|
|
1182
|
+
create: async (body) => {
|
|
1183
|
+
calls.push({ method: "realtime.clientSecrets.create", body });
|
|
1184
|
+
return {
|
|
1185
|
+
expires_at: 123,
|
|
1186
|
+
session: body.session,
|
|
1187
|
+
value: "ek_rt_secret_1",
|
|
1188
|
+
};
|
|
1189
|
+
},
|
|
1190
|
+
},
|
|
1191
|
+
calls: {
|
|
1192
|
+
accept: async (callId, body) => {
|
|
1193
|
+
calls.push({ method: "realtime.calls.accept", callId, body });
|
|
1194
|
+
},
|
|
1195
|
+
hangup: async (callId, options) => {
|
|
1196
|
+
calls.push({ method: "realtime.calls.hangup", callId, options });
|
|
1197
|
+
},
|
|
1198
|
+
refer: async (callId, body) => {
|
|
1199
|
+
calls.push({ method: "realtime.calls.refer", callId, body });
|
|
1200
|
+
},
|
|
1201
|
+
reject: async (callId, body) => {
|
|
1202
|
+
calls.push({ method: "realtime.calls.reject", callId, body });
|
|
1203
|
+
},
|
|
1204
|
+
},
|
|
1205
|
+
};
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
await withMockedOpenAI(FakeOpenAI, async () => {
|
|
1210
|
+
const modulePath = require.resolve("../src/realtime/methods.js");
|
|
1211
|
+
delete require.cache[modulePath];
|
|
1212
|
+
const realtimeMethods = require("../src/realtime/methods.js");
|
|
1213
|
+
|
|
1214
|
+
const clientContext = { clientParams: { apiKey: "sk-test" } };
|
|
1215
|
+
|
|
1216
|
+
const clientSecretPayload = {
|
|
1217
|
+
expires_after: {
|
|
1218
|
+
anchor: "created_at",
|
|
1219
|
+
seconds: 600,
|
|
1220
|
+
},
|
|
1221
|
+
session: {
|
|
1222
|
+
type: "realtime",
|
|
1223
|
+
model: "gpt-realtime-1.5",
|
|
1224
|
+
instructions: "Speak clearly and keep responses concise.",
|
|
1225
|
+
output_modalities: ["audio"],
|
|
1226
|
+
},
|
|
1227
|
+
};
|
|
1228
|
+
const secret = await realtimeMethods.createRealtimeClientSecret.call(clientContext, {
|
|
1229
|
+
payload: clientSecretPayload,
|
|
1230
|
+
});
|
|
1231
|
+
assert.deepEqual(secret, {
|
|
1232
|
+
expires_at: 123,
|
|
1233
|
+
session: clientSecretPayload.session,
|
|
1234
|
+
value: "ek_rt_secret_1",
|
|
1235
|
+
});
|
|
1236
|
+
|
|
1237
|
+
const accepted = await realtimeMethods.acceptRealtimeCall.call(clientContext, {
|
|
1238
|
+
payload: {
|
|
1239
|
+
call_id: "call_1",
|
|
1240
|
+
type: "realtime",
|
|
1241
|
+
model: "gpt-audio-1.5",
|
|
1242
|
+
output_modalities: ["audio"],
|
|
1243
|
+
},
|
|
1244
|
+
});
|
|
1245
|
+
assert.deepEqual(accepted, { call_id: "call_1", status: "accepted" });
|
|
1246
|
+
|
|
1247
|
+
const hungUp = await realtimeMethods.hangupRealtimeCall.call(clientContext, {
|
|
1248
|
+
payload: { call_id: "call_1" },
|
|
1249
|
+
});
|
|
1250
|
+
assert.deepEqual(hungUp, { call_id: "call_1", status: "hung_up" });
|
|
1251
|
+
|
|
1252
|
+
const referred = await realtimeMethods.referRealtimeCall.call(clientContext, {
|
|
1253
|
+
payload: { call_id: "call_1", target_uri: "tel:+14155550123" },
|
|
1254
|
+
});
|
|
1255
|
+
assert.deepEqual(referred, { call_id: "call_1", status: "referred" });
|
|
1256
|
+
|
|
1257
|
+
const rejected = await realtimeMethods.rejectRealtimeCall.call(clientContext, {
|
|
1258
|
+
payload: { call_id: "call_2", status_code: 486 },
|
|
1259
|
+
});
|
|
1260
|
+
assert.deepEqual(rejected, { call_id: "call_2", status: "rejected" });
|
|
1261
|
+
|
|
1262
|
+
delete require.cache[modulePath];
|
|
1263
|
+
});
|
|
1264
|
+
|
|
1265
|
+
const realtimeCalls = calls.filter((entry) => entry.method !== "ctor");
|
|
1266
|
+
assert.deepEqual(realtimeCalls, [
|
|
1267
|
+
{
|
|
1268
|
+
method: "realtime.clientSecrets.create",
|
|
1269
|
+
body: {
|
|
1270
|
+
expires_after: {
|
|
1271
|
+
anchor: "created_at",
|
|
1272
|
+
seconds: 600,
|
|
1273
|
+
},
|
|
1274
|
+
session: {
|
|
1275
|
+
type: "realtime",
|
|
1276
|
+
model: "gpt-realtime-1.5",
|
|
1277
|
+
instructions: "Speak clearly and keep responses concise.",
|
|
1278
|
+
output_modalities: ["audio"],
|
|
1279
|
+
},
|
|
1280
|
+
},
|
|
1281
|
+
},
|
|
1282
|
+
{
|
|
1283
|
+
method: "realtime.calls.accept",
|
|
1284
|
+
callId: "call_1",
|
|
1285
|
+
body: {
|
|
1286
|
+
type: "realtime",
|
|
1287
|
+
model: "gpt-audio-1.5",
|
|
1288
|
+
output_modalities: ["audio"],
|
|
1289
|
+
},
|
|
1290
|
+
},
|
|
1291
|
+
{
|
|
1292
|
+
method: "realtime.calls.hangup",
|
|
1293
|
+
callId: "call_1",
|
|
1294
|
+
options: {},
|
|
1295
|
+
},
|
|
1296
|
+
{
|
|
1297
|
+
method: "realtime.calls.refer",
|
|
1298
|
+
callId: "call_1",
|
|
1299
|
+
body: { target_uri: "tel:+14155550123" },
|
|
1300
|
+
},
|
|
1301
|
+
{
|
|
1302
|
+
method: "realtime.calls.reject",
|
|
1303
|
+
callId: "call_2",
|
|
1304
|
+
body: { status_code: 486 },
|
|
1305
|
+
},
|
|
1306
|
+
]);
|
|
1307
|
+
});
|
|
1308
|
+
|
|
1309
|
+
test("webhooks methods map to OpenAI SDK webhooks utilities", async () => {
|
|
1310
|
+
const calls = [];
|
|
1311
|
+
|
|
1312
|
+
class FakeOpenAI {
|
|
1313
|
+
constructor(clientParams) {
|
|
1314
|
+
calls.push({ method: "ctor", clientParams });
|
|
1315
|
+
this.webhooks = {
|
|
1316
|
+
unwrap: async (payload, headers, secret, tolerance) => {
|
|
1317
|
+
calls.push({
|
|
1318
|
+
method: "webhooks.unwrap",
|
|
1319
|
+
payload,
|
|
1320
|
+
headers,
|
|
1321
|
+
secret,
|
|
1322
|
+
tolerance,
|
|
1323
|
+
});
|
|
1324
|
+
return { id: "evt_1", type: "response.completed" };
|
|
1325
|
+
},
|
|
1326
|
+
verifySignature: async (payload, headers, secret, tolerance) => {
|
|
1327
|
+
calls.push({
|
|
1328
|
+
method: "webhooks.verifySignature",
|
|
1329
|
+
payload,
|
|
1330
|
+
headers,
|
|
1331
|
+
secret,
|
|
1332
|
+
tolerance,
|
|
1333
|
+
});
|
|
1334
|
+
},
|
|
1335
|
+
};
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
await withMockedOpenAI(FakeOpenAI, async () => {
|
|
1340
|
+
const modulePath = require.resolve("../src/webhooks/methods.js");
|
|
1341
|
+
delete require.cache[modulePath];
|
|
1342
|
+
const webhookMethods = require("../src/webhooks/methods.js");
|
|
1343
|
+
|
|
1344
|
+
const clientContext = { clientParams: { apiKey: "sk-test" } };
|
|
1345
|
+
|
|
1346
|
+
const event = await webhookMethods.unwrapWebhookEvent.call(clientContext, {
|
|
1347
|
+
payload: {
|
|
1348
|
+
payload: "{\"id\":\"evt_1\"}",
|
|
1349
|
+
headers: {
|
|
1350
|
+
"webhook-id": "wh_1",
|
|
1351
|
+
"webhook-signature": "v1,abc",
|
|
1352
|
+
"webhook-timestamp": "123",
|
|
1353
|
+
},
|
|
1354
|
+
secret: "whsec_test",
|
|
1355
|
+
tolerance: 30,
|
|
1356
|
+
},
|
|
1357
|
+
});
|
|
1358
|
+
assert.deepEqual(event, { id: "evt_1", type: "response.completed" });
|
|
1359
|
+
|
|
1360
|
+
const verified = await webhookMethods.verifyWebhookSignature.call(clientContext, {
|
|
1361
|
+
payload: {
|
|
1362
|
+
payload: "{\"id\":\"evt_1\"}",
|
|
1363
|
+
headers: {
|
|
1364
|
+
"webhook-id": "wh_1",
|
|
1365
|
+
"webhook-signature": "v1,abc",
|
|
1366
|
+
"webhook-timestamp": "123",
|
|
1367
|
+
},
|
|
1368
|
+
secret: "whsec_test",
|
|
1369
|
+
tolerance: 30,
|
|
1370
|
+
},
|
|
1371
|
+
});
|
|
1372
|
+
assert.deepEqual(verified, { verified: true });
|
|
1373
|
+
|
|
1374
|
+
delete require.cache[modulePath];
|
|
1375
|
+
});
|
|
1376
|
+
|
|
1377
|
+
const webhookCalls = calls.filter((entry) => entry.method !== "ctor");
|
|
1378
|
+
assert.deepEqual(webhookCalls, [
|
|
1379
|
+
{
|
|
1380
|
+
method: "webhooks.unwrap",
|
|
1381
|
+
payload: "{\"id\":\"evt_1\"}",
|
|
1382
|
+
headers: {
|
|
1383
|
+
"webhook-id": "wh_1",
|
|
1384
|
+
"webhook-signature": "v1,abc",
|
|
1385
|
+
"webhook-timestamp": "123",
|
|
1386
|
+
},
|
|
1387
|
+
secret: "whsec_test",
|
|
1388
|
+
tolerance: 30,
|
|
1389
|
+
},
|
|
1390
|
+
{
|
|
1391
|
+
method: "webhooks.verifySignature",
|
|
1392
|
+
payload: "{\"id\":\"evt_1\"}",
|
|
1393
|
+
headers: {
|
|
1394
|
+
"webhook-id": "wh_1",
|
|
1395
|
+
"webhook-signature": "v1,abc",
|
|
1396
|
+
"webhook-timestamp": "123",
|
|
1397
|
+
},
|
|
1398
|
+
secret: "whsec_test",
|
|
1399
|
+
tolerance: 30,
|
|
1400
|
+
},
|
|
1401
|
+
]);
|
|
1402
|
+
});
|
|
1403
|
+
|
|
1404
|
+
test("OpenaiApi prototype exposes latest methods", () => {
|
|
1405
|
+
const OpenaiApi = require("../src/lib.js");
|
|
1406
|
+
const client = new OpenaiApi("sk-test", "https://api.openai.com/v1", null);
|
|
1407
|
+
|
|
1408
|
+
assert.equal(typeof client.cancelModelResponse, "function");
|
|
1409
|
+
assert.equal(typeof client.compactModelResponse, "function");
|
|
1410
|
+
assert.equal(typeof client.countInputTokens, "function");
|
|
1411
|
+
assert.equal(typeof client.createConversation, "function");
|
|
1412
|
+
assert.equal(typeof client.listConversationItems, "function");
|
|
1413
|
+
assert.equal(typeof client.createEval, "function");
|
|
1414
|
+
assert.equal(typeof client.listEvalRunOutputItems, "function");
|
|
1415
|
+
assert.equal(typeof client.createRealtimeClientSecret, "function");
|
|
1416
|
+
assert.equal(typeof client.listSkills, "function");
|
|
1417
|
+
assert.equal(typeof client.getSkillVersionContent, "function");
|
|
1418
|
+
assert.equal(typeof client.createVideo, "function");
|
|
1419
|
+
assert.equal(typeof client.verifyWebhookSignature, "function");
|
|
1420
|
+
});
|
|
1421
|
+
|
|
1422
|
+
test("editor templates and locale expose latest methods", () => {
|
|
1423
|
+
const responsesTemplate = fs.readFileSync(
|
|
1424
|
+
path.join(__dirname, "..", "src", "responses", "template.html"),
|
|
1425
|
+
"utf8"
|
|
1426
|
+
);
|
|
1427
|
+
const conversationsTemplate = fs.readFileSync(
|
|
1428
|
+
path.join(__dirname, "..", "src", "conversations", "template.html"),
|
|
1429
|
+
"utf8"
|
|
1430
|
+
);
|
|
1431
|
+
const skillsTemplate = fs.readFileSync(
|
|
1432
|
+
path.join(__dirname, "..", "src", "skills", "template.html"),
|
|
1433
|
+
"utf8"
|
|
1434
|
+
);
|
|
1435
|
+
const evalsTemplate = fs.readFileSync(
|
|
1436
|
+
path.join(__dirname, "..", "src", "evals", "template.html"),
|
|
1437
|
+
"utf8"
|
|
1438
|
+
);
|
|
1439
|
+
const realtimeTemplate = fs.readFileSync(
|
|
1440
|
+
path.join(__dirname, "..", "src", "realtime", "template.html"),
|
|
1441
|
+
"utf8"
|
|
1442
|
+
);
|
|
1443
|
+
const videosTemplate = fs.readFileSync(
|
|
1444
|
+
path.join(__dirname, "..", "src", "videos", "template.html"),
|
|
1445
|
+
"utf8"
|
|
1446
|
+
);
|
|
1447
|
+
const webhooksTemplate = fs.readFileSync(
|
|
1448
|
+
path.join(__dirname, "..", "src", "webhooks", "template.html"),
|
|
1449
|
+
"utf8"
|
|
1450
|
+
);
|
|
1451
|
+
const nodeTemplate = fs.readFileSync(
|
|
1452
|
+
path.join(__dirname, "..", "src", "node.html"),
|
|
1453
|
+
"utf8"
|
|
1454
|
+
);
|
|
1455
|
+
const responsesHelp = fs.readFileSync(
|
|
1456
|
+
path.join(__dirname, "..", "src", "responses", "help.html"),
|
|
1457
|
+
"utf8"
|
|
1458
|
+
);
|
|
1459
|
+
const skillsHelp = fs.readFileSync(
|
|
1460
|
+
path.join(__dirname, "..", "src", "skills", "help.html"),
|
|
1461
|
+
"utf8"
|
|
1462
|
+
);
|
|
1463
|
+
const evalsHelp = fs.readFileSync(
|
|
1464
|
+
path.join(__dirname, "..", "src", "evals", "help.html"),
|
|
1465
|
+
"utf8"
|
|
1466
|
+
);
|
|
1467
|
+
const realtimeHelp = fs.readFileSync(
|
|
1468
|
+
path.join(__dirname, "..", "src", "realtime", "help.html"),
|
|
1469
|
+
"utf8"
|
|
1470
|
+
);
|
|
1471
|
+
const videosHelp = fs.readFileSync(
|
|
1472
|
+
path.join(__dirname, "..", "src", "videos", "help.html"),
|
|
1473
|
+
"utf8"
|
|
1474
|
+
);
|
|
1475
|
+
const webhooksHelp = fs.readFileSync(
|
|
1476
|
+
path.join(__dirname, "..", "src", "webhooks", "help.html"),
|
|
1477
|
+
"utf8"
|
|
1478
|
+
);
|
|
1479
|
+
const locale = JSON.parse(
|
|
1480
|
+
fs.readFileSync(path.join(__dirname, "..", "locales", "en-US", "node.json"), "utf8")
|
|
1481
|
+
);
|
|
1482
|
+
|
|
1483
|
+
assert.match(responsesTemplate, /value="cancelModelResponse"/);
|
|
1484
|
+
assert.match(responsesTemplate, /value="compactModelResponse"/);
|
|
1485
|
+
assert.match(responsesTemplate, /value="countInputTokens"/);
|
|
1486
|
+
assert.match(conversationsTemplate, /value="createConversation"/);
|
|
1487
|
+
assert.match(conversationsTemplate, /value="listConversationItems"/);
|
|
1488
|
+
assert.match(skillsTemplate, /value="listSkills"/);
|
|
1489
|
+
assert.match(skillsTemplate, /value="getSkillVersionContent"/);
|
|
1490
|
+
assert.match(evalsTemplate, /value="createEval"/);
|
|
1491
|
+
assert.match(evalsTemplate, /value="listEvalRunOutputItems"/);
|
|
1492
|
+
assert.match(realtimeTemplate, /value="createRealtimeClientSecret"/);
|
|
1493
|
+
assert.match(realtimeTemplate, /value="rejectRealtimeCall"/);
|
|
1494
|
+
assert.match(videosTemplate, /value="downloadVideoContent"/);
|
|
1495
|
+
assert.match(webhooksTemplate, /value="verifyWebhookSignature"/);
|
|
1496
|
+
assert.match(nodeTemplate, /@@include\('\.\/conversations\/template\.html'\)/);
|
|
1497
|
+
assert.match(nodeTemplate, /@@include\('\.\/conversations\/help\.html'\)/);
|
|
1498
|
+
assert.match(nodeTemplate, /@@include\('\.\/evals\/template\.html'\)/);
|
|
1499
|
+
assert.match(nodeTemplate, /@@include\('\.\/evals\/help\.html'\)/);
|
|
1500
|
+
assert.match(nodeTemplate, /@@include\('\.\/realtime\/template\.html'\)/);
|
|
1501
|
+
assert.match(nodeTemplate, /@@include\('\.\/realtime\/help\.html'\)/);
|
|
1502
|
+
assert.match(nodeTemplate, /@@include\('\.\/skills\/template\.html'\)/);
|
|
1503
|
+
assert.match(nodeTemplate, /@@include\('\.\/skills\/help\.html'\)/);
|
|
1504
|
+
assert.match(nodeTemplate, /@@include\('\.\/videos\/template\.html'\)/);
|
|
1505
|
+
assert.match(nodeTemplate, /@@include\('\.\/videos\/help\.html'\)/);
|
|
1506
|
+
assert.match(nodeTemplate, /@@include\('\.\/webhooks\/template\.html'\)/);
|
|
1507
|
+
assert.match(nodeTemplate, /@@include\('\.\/webhooks\/help\.html'\)/);
|
|
1508
|
+
assert.match(responsesHelp, /⋙ Count Input Tokens/);
|
|
1509
|
+
assert.match(responsesHelp, /Default is <code>desc<\/code>/);
|
|
1510
|
+
assert.match(evalsHelp, /⋙ Create Eval/);
|
|
1511
|
+
assert.match(evalsHelp, /⋙ List Eval Run Output Items/);
|
|
1512
|
+
assert.match(realtimeHelp, /⋙ Create Realtime Client Secret/);
|
|
1513
|
+
assert.match(realtimeHelp, /⋙ Reject Realtime Call/);
|
|
1514
|
+
assert.match(realtimeHelp, /msg\.payload\.session/);
|
|
1515
|
+
assert.match(realtimeHelp, /session\.model/);
|
|
1516
|
+
assert.match(realtimeHelp, /gpt-realtime-1\.5/);
|
|
1517
|
+
assert.match(realtimeHelp, /gpt-audio-1\.5/);
|
|
1518
|
+
assert.match(skillsHelp, /⋙ Create Skill/);
|
|
1519
|
+
assert.match(skillsHelp, /⋙ List Skill Versions/);
|
|
1520
|
+
assert.match(videosHelp, /⋙ Download Video Content/);
|
|
1521
|
+
assert.match(webhooksHelp, /⋙ Verify Webhook Signature/);
|
|
1522
|
+
|
|
1523
|
+
assert.equal(
|
|
1524
|
+
locale.OpenaiApi.parameters.cancelModelResponse,
|
|
1525
|
+
"cancel model response"
|
|
1526
|
+
);
|
|
1527
|
+
assert.equal(
|
|
1528
|
+
locale.OpenaiApi.parameters.countInputTokens,
|
|
1529
|
+
"count input tokens"
|
|
1530
|
+
);
|
|
1531
|
+
assert.equal(
|
|
1532
|
+
locale.OpenaiApi.parameters.createConversation,
|
|
1533
|
+
"create conversation"
|
|
1534
|
+
);
|
|
1535
|
+
assert.equal(
|
|
1536
|
+
locale.OpenaiApi.parameters.listSkills,
|
|
1537
|
+
"list skills"
|
|
1538
|
+
);
|
|
1539
|
+
assert.equal(
|
|
1540
|
+
locale.OpenaiApi.parameters.getSkillVersion,
|
|
1541
|
+
"retrieve skill version"
|
|
1542
|
+
);
|
|
1543
|
+
assert.equal(
|
|
1544
|
+
locale.OpenaiApi.parameters.createEvalRun,
|
|
1545
|
+
"create eval run"
|
|
1546
|
+
);
|
|
1547
|
+
assert.equal(
|
|
1548
|
+
locale.OpenaiApi.parameters.acceptRealtimeCall,
|
|
1549
|
+
"accept realtime call"
|
|
1550
|
+
);
|
|
1551
|
+
assert.equal(
|
|
1552
|
+
locale.OpenaiApi.parameters.downloadVideoContent,
|
|
1553
|
+
"download video content"
|
|
1554
|
+
);
|
|
1555
|
+
assert.equal(
|
|
1556
|
+
locale.OpenaiApi.parameters.verifyWebhookSignature,
|
|
1557
|
+
"verify webhook signature"
|
|
1558
|
+
);
|
|
1559
|
+
});
|