




















@@ -0,0 +1,168 @@
1+import fs from "node:fs";
2+import http from "node:http";
3+4+const port = Number(process.env.MOCK_PORT ?? process.env.OPENCLAW_MOCK_OPENAI_PORT);
5+const successMarker = process.env.SUCCESS_MARKER ?? "OPENCLAW_E2E_OK";
6+const requestLog = process.env.MOCK_REQUEST_LOG;
7+8+if (!Number.isInteger(port) || port <= 0) {
9+throw new Error("missing valid MOCK_PORT or OPENCLAW_MOCK_OPENAI_PORT");
10+}
11+12+function readBody(req) {
13+return new Promise((resolve, reject) => {
14+let body = "";
15+req.setEncoding("utf8");
16+req.on("data", (chunk) => {
17+body += chunk;
18+});
19+req.on("end", () => resolve(body));
20+req.on("error", reject);
21+});
22+}
23+24+function writeJson(res, status, body) {
25+res.writeHead(status, { "content-type": "application/json" });
26+res.end(JSON.stringify(body));
27+}
28+29+function responseEvents(text) {
30+return [
31+{
32+type: "response.output_item.added",
33+item: {
34+type: "message",
35+id: "msg_e2e_1",
36+role: "assistant",
37+content: [],
38+status: "in_progress",
39+},
40+},
41+{
42+type: "response.output_item.done",
43+item: {
44+type: "message",
45+id: "msg_e2e_1",
46+role: "assistant",
47+status: "completed",
48+content: [{ type: "output_text", text, annotations: [] }],
49+},
50+},
51+{
52+type: "response.completed",
53+response: {
54+status: "completed",
55+usage: {
56+input_tokens: 11,
57+output_tokens: 7,
58+total_tokens: 18,
59+input_tokens_details: { cached_tokens: 0 },
60+},
61+},
62+},
63+];
64+}
65+66+function writeSse(res, events) {
67+res.writeHead(200, {
68+"content-type": "text/event-stream",
69+"cache-control": "no-store",
70+connection: "keep-alive",
71+});
72+for (const event of events) {
73+res.write(`data: ${JSON.stringify(event)}\n\n`);
74+}
75+res.write("data: [DONE]\n\n");
76+res.end();
77+}
78+79+function writeChatCompletion(res, stream) {
80+if (stream) {
81+writeSse(res, [
82+{
83+id: "chatcmpl_e2e",
84+object: "chat.completion.chunk",
85+choices: [{ index: 0, delta: { role: "assistant", content: successMarker } }],
86+},
87+{
88+id: "chatcmpl_e2e",
89+object: "chat.completion.chunk",
90+choices: [{ index: 0, delta: {}, finish_reason: "stop" }],
91+},
92+]);
93+return;
94+}
95+writeJson(res, 200, {
96+id: "chatcmpl_e2e",
97+object: "chat.completion",
98+choices: [
99+{ index: 0, message: { role: "assistant", content: successMarker }, finish_reason: "stop" },
100+],
101+usage: { prompt_tokens: 11, completion_tokens: 7, total_tokens: 18 },
102+});
103+}
104+105+const server = http.createServer(async (req, res) => {
106+const url = new URL(req.url ?? "/", "http://127.0.0.1");
107+if (req.method === "GET" && url.pathname === "/health") {
108+writeJson(res, 200, { ok: true });
109+return;
110+}
111+if (req.method === "GET" && url.pathname === "/v1/models") {
112+writeJson(res, 200, {
113+object: "list",
114+data: [{ id: "gpt-5.4", object: "model", owned_by: "openclaw-e2e" }],
115+});
116+return;
117+}
118+119+const bodyText = await readBody(req);
120+if (requestLog) {
121+fs.appendFileSync(
122+requestLog,
123+`${JSON.stringify({ method: req.method, path: url.pathname, body: bodyText })}\n`,
124+);
125+}
126+let body = {};
127+try {
128+body = bodyText ? JSON.parse(bodyText) : {};
129+} catch {
130+body = {};
131+}
132+133+if (req.method === "POST" && url.pathname === "/v1/responses") {
134+if (body.stream === false) {
135+writeJson(res, 200, {
136+id: "resp_e2e",
137+object: "response",
138+status: "completed",
139+output: [
140+{
141+type: "message",
142+id: "msg_e2e_1",
143+role: "assistant",
144+status: "completed",
145+content: [{ type: "output_text", text: successMarker, annotations: [] }],
146+},
147+],
148+usage: { input_tokens: 11, output_tokens: 7, total_tokens: 18 },
149+});
150+return;
151+}
152+writeSse(res, responseEvents(successMarker));
153+return;
154+}
155+156+if (req.method === "POST" && url.pathname === "/v1/chat/completions") {
157+writeChatCompletion(res, body.stream !== false);
158+return;
159+}
160+161+writeJson(res, 404, {
162+error: { message: `unhandled mock route: ${req.method} ${url.pathname}` },
163+});
164+});
165+166+server.listen(port, "127.0.0.1", () => {
167+console.log(`mock-openai listening on ${port}`);
168+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。