Skip to content

Commit 410f04d

Browse files
committed
test: prove defer loading parity for NOA-36
1 parent ebb312f commit 410f04d

3 files changed

Lines changed: 136 additions & 2 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ If you want to understand the shape of this node quickly, these example flows ar
138138
This repository currently includes:
139139

140140
- ChatKit / Agent Builder support, including session creation and cancellation, plus thread and thread-item inspection for published workflows
141-
- Responses API support, including current SDK-typed model ids such as `gpt-5.4-mini`, `gpt-5.4-nano`, and dated variants like `gpt-5.4-mini-2026-03-17`, plus `phase`, `prompt_cache_key`, `tool_search`, GA computer-use payloads, parse and stream helpers, cancellation, compaction, input-token counting, and websocket mode
141+
- Responses API support, including current SDK-typed model ids such as `gpt-5.4-mini`, `gpt-5.4-nano`, and dated variants like `gpt-5.4-mini-2026-03-17`, plus `phase`, `prompt_cache_key`, `tool_search`, deferred MCP loading via `defer_loading`, GA computer-use payloads, parse and stream helpers, cancellation, compaction, input-token counting, and websocket mode
142142
- Vector Stores support, including direct vector-store search, vector-store file attribute updates, parsed file-content retrieval, and file-attribute filters using `ComparisonFilter` operators such as `in` and `nin`
143143
- Realtime API support, including client-secret creation, SIP call operations, and current SDK-typed model ids such as `gpt-realtime-1.5` and `gpt-audio-1.5`
144144
- Audio speech support with built-in voices and saved custom voice ids

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
"build:html": "gulp build-html",
3333
"build:js": "esbuild src/lib.js --bundle --outfile=lib.js --format=cjs --platform=node",
3434
"build": "npm run build:html && npm run build:js",
35-
"test": "node --test test/service-host-node.test.js test/service-host-editor-template.test.js test/openai-node-auth-routing.test.js test/openai-methods-mapping.test.js test/openai-responses-websocket.test.js test/chatkit-docs-examples.test.js test/videos-docs-examples.test.js test/examples-service-host-neutrality.test.js test/audio-docs-parity.test.js test/batch-docs-parity.test.js test/comparison-filter-parity.test.js test/vector-store-family-parity.test.js test/model-slug-parity.test.js",
35+
"test": "node --test test/service-host-node.test.js test/service-host-editor-template.test.js test/openai-node-auth-routing.test.js test/openai-methods-mapping.test.js test/openai-responses-websocket.test.js test/chatkit-docs-examples.test.js test/videos-docs-examples.test.js test/examples-service-host-neutrality.test.js test/audio-docs-parity.test.js test/batch-docs-parity.test.js test/comparison-filter-parity.test.js test/vector-store-family-parity.test.js test/model-slug-parity.test.js test/defer-loading-parity.test.js",
3636
"prepare": "npm run build"
3737
},
3838
"dependencies": {

test/defer-loading-parity.test.js

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
"use strict";
2+
3+
// This file keeps the defer_loading tool contract honest.
4+
// It proves the node forwards deferred MCP tool definitions unchanged and that the docs/examples still describe the current Responses tool-search shape.
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+
const readme = fs.readFileSync(path.join(__dirname, "..", "README.md"), "utf8");
36+
const responsesHelp = fs.readFileSync(
37+
path.join(__dirname, "..", "src", "responses", "help.html"),
38+
"utf8"
39+
);
40+
const toolSearchExample = JSON.parse(
41+
fs.readFileSync(
42+
path.join(__dirname, "..", "examples", "responses", "tool-search.json"),
43+
"utf8"
44+
)
45+
);
46+
47+
test("responses create forwards deferred MCP tool definitions unchanged", async () => {
48+
const calls = [];
49+
const requestPayload = {
50+
model: "gpt-5.4-mini",
51+
tools: [
52+
{ type: "tool_search" },
53+
{
54+
type: "mcp",
55+
server_label: "deepwiki",
56+
server_url: "https://mcp.deepwiki.com/mcp",
57+
require_approval: "never",
58+
defer_loading: true,
59+
},
60+
],
61+
input: "Use the available documentation tools to summarize MCP transports.",
62+
};
63+
64+
class FakeOpenAI {
65+
constructor(clientParams) {
66+
calls.push({ method: "ctor", clientParams });
67+
this.responses = {
68+
create: async (payload) => {
69+
calls.push({ method: "responses.create", payload });
70+
return { id: "resp_deferred", status: "completed" };
71+
},
72+
};
73+
}
74+
}
75+
76+
await withMockedOpenAI(FakeOpenAI, async () => {
77+
const modulePath = require.resolve("../src/responses/methods.js");
78+
delete require.cache[modulePath];
79+
const responsesMethods = require("../src/responses/methods.js");
80+
81+
const clientContext = {
82+
clientParams: {
83+
apiKey: "sk-test",
84+
baseURL: "https://api.example.com/v1",
85+
},
86+
};
87+
88+
const response = await responsesMethods.createModelResponse.call(clientContext, {
89+
payload: requestPayload,
90+
});
91+
92+
assert.deepEqual(response, { id: "resp_deferred", status: "completed" });
93+
94+
delete require.cache[modulePath];
95+
});
96+
97+
assert.deepEqual(calls.filter((entry) => entry.method !== "ctor"), [
98+
{
99+
method: "responses.create",
100+
payload: requestPayload,
101+
},
102+
]);
103+
});
104+
105+
test("tool-search docs and example keep defer_loading explicit", () => {
106+
assert.match(readme, /deferred MCP loading via `defer_loading`/);
107+
assert.match(responsesHelp, /defer_loading: true/);
108+
assert.match(responsesHelp, /Deferred tool loading is supported/);
109+
110+
const injectNode = toolSearchExample.find(
111+
(entry) => entry.type === "inject" && entry.name === "Create Tool Search Request"
112+
);
113+
assert.ok(injectNode);
114+
115+
const toolSearchTool = JSON.parse(
116+
injectNode.props.find((prop) => prop.p === "ai.tools[0]").v
117+
);
118+
const deferredMcpTool = JSON.parse(
119+
injectNode.props.find((prop) => prop.p === "ai.tools[1]").v
120+
);
121+
122+
assert.deepEqual(toolSearchTool, { type: "tool_search" });
123+
assert.deepEqual(deferredMcpTool, {
124+
type: "mcp",
125+
server_label: "deepwiki",
126+
server_url: "https://mcp.deepwiki.com/mcp",
127+
require_approval: "never",
128+
defer_loading: true,
129+
});
130+
131+
const exampleTab = toolSearchExample.find((entry) => entry.type === "tab");
132+
assert.ok(exampleTab);
133+
assert.match(exampleTab.info, /defer_loading: true/);
134+
});

0 commit comments

Comments
 (0)