





























@@ -2,6 +2,97 @@ import fs from "node:fs";
2233const command = process.argv[2];
445+const ERROR_DETAIL_TAIL_BYTES = 64 * 1024;
6+const REQUEST_LOG_SCAN_CHUNK_BYTES = 64 * 1024;
7+const RESPONSE_PREVIEW_BYTES = 8 * 1024;
8+const RESPONSE_PREVIEW_COUNT = 5;
9+10+function tailText(text, maxBytes = ERROR_DETAIL_TAIL_BYTES) {
11+if (Buffer.byteLength(text, "utf8") <= maxBytes) {
12+return text;
13+}
14+return Buffer.from(text, "utf8").subarray(-maxBytes).toString("utf8");
15+}
16+17+function readTextFileTail(file, maxBytes = ERROR_DETAIL_TAIL_BYTES) {
18+let stat;
19+try {
20+stat = fs.statSync(file);
21+} catch {
22+return "";
23+}
24+if (!stat.isFile() || stat.size <= 0) {
25+return "";
26+}
27+28+const length = Math.min(maxBytes, stat.size);
29+const start = stat.size - length;
30+const fd = fs.openSync(file, "r");
31+try {
32+const buffer = Buffer.alloc(length);
33+const bytesRead = fs.readSync(fd, buffer, 0, length, start);
34+return buffer.subarray(0, bytesRead).toString("utf8");
35+} finally {
36+fs.closeSync(fd);
37+}
38+}
39+40+function scanTextFileLines(file, onLine) {
41+const fd = fs.openSync(file, "r");
42+try {
43+const buffer = Buffer.alloc(REQUEST_LOG_SCAN_CHUNK_BYTES);
44+let carry = "";
45+let lineNumber = 1;
46+while (true) {
47+const bytesRead = fs.readSync(fd, buffer, 0, buffer.length, null);
48+if (bytesRead <= 0) {
49+break;
50+}
51+const text = carry + buffer.subarray(0, bytesRead).toString("utf8");
52+const lines = text.split(/\r?\n/u);
53+carry = lines.pop() ?? "";
54+for (const line of lines) {
55+onLine(line, lineNumber);
56+lineNumber += 1;
57+}
58+}
59+if (carry.length > 0) {
60+onLine(carry, lineNumber);
61+}
62+} finally {
63+fs.closeSync(fd);
64+}
65+}
66+67+function scanSuccessRequest(logPath) {
68+let responseCount = 0;
69+let success;
70+const recentResponses = [];
71+scanTextFileLines(logPath, (line, lineNumber) => {
72+const trimmed = line.trim();
73+if (!trimmed) {
74+return;
75+}
76+const entry = JSON.parse(trimmed);
77+if (entry.path !== "/v1/responses") {
78+return;
79+}
80+responseCount += 1;
81+const bodyText = JSON.stringify(entry.body);
82+if (recentResponses.length >= RESPONSE_PREVIEW_COUNT) {
83+recentResponses.shift();
84+}
85+recentResponses.push({
86+line: lineNumber,
87+bodyTail: tailText(bodyText, RESPONSE_PREVIEW_BYTES),
88+});
89+if (!success && bodyText.includes("OPENCLAW_SCHEMA_E2E_OK")) {
90+success = entry;
91+}
92+});
93+return { responseCount, success, recentResponses };
94+}
95+596function assertPatchBehavior() {
697return import("../../../../dist/extensions/openai/native-web-search.js").then(
798({ patchOpenAINativeWebSearchPayload }) => {
@@ -40,21 +131,16 @@ function assertPatchBehavior() {
4013141132function assertSuccessRequest() {
42133const logPath = process.argv[3];
43-const entries = fs
44-.readFileSync(logPath, "utf8")
45-.trim()
46-.split(/\n+/u)
47-.filter(Boolean)
48-.map((line) => JSON.parse(line));
49-const responseEntries = entries.filter((entry) => entry.path === "/v1/responses");
50-if (responseEntries.length < 1) {
51-throw new Error(`mock OpenAI /v1/responses was not used. Requests: ${JSON.stringify(entries)}`);
134+const { responseCount, success, recentResponses } = scanSuccessRequest(logPath);
135+if (responseCount < 1) {
136+throw new Error(
137+`mock OpenAI /v1/responses was not used. Request log tail: ${readTextFileTail(logPath)}`,
138+);
52139}
53-const success = responseEntries.find((entry) =>
54-JSON.stringify(entry.body).includes("OPENCLAW_SCHEMA_E2E_OK"),
55-);
56140if (!success) {
57-throw new Error(`missing success request. Requests: ${JSON.stringify(responseEntries)}`);
141+throw new Error(
142+`missing success request. Recent /v1/responses: ${JSON.stringify(recentResponses)}`,
143+);
58144}
59145const tools = Array.isArray(success.body.tools) ? success.body.tools : [];
60146const hasWebSearch = tools.some(
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。