@reconcrap/boss-recommend-mcp 2.0.9 → 2.0.11
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/package.json +1 -1
- package/src/core/capture/index.js +143 -1
- package/src/core/cv-acquisition/index.js +5 -0
- package/src/core/screening/index.js +59 -14
- package/src/domains/chat/run-service.js +10 -5
- package/src/domains/recommend/refresh.js +104 -82
- package/src/domains/recommend/run-service.js +4 -0
- package/src/domains/recruit/run-service.js +4 -0
package/package.json
CHANGED
|
@@ -146,6 +146,14 @@ function filePathForSequence(basePath, index, extension) {
|
|
|
146
146
|
return path.join(parsed.dir, `${parsed.name}-page-${page}${parsed.ext || `.${extension}`}`);
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
+
function filePathForLlmSequence(basePath, index) {
|
|
150
|
+
const resolved = resolveOutputPath(basePath);
|
|
151
|
+
if (!resolved) return null;
|
|
152
|
+
const parsed = path.parse(resolved);
|
|
153
|
+
const page = String(index + 1).padStart(2, "0");
|
|
154
|
+
return path.join(parsed.dir, `${parsed.name}-llm-${page}.jpg`);
|
|
155
|
+
}
|
|
156
|
+
|
|
149
157
|
function screenshotHash(buffer) {
|
|
150
158
|
return crypto.createHash("sha256").update(buffer).digest("hex");
|
|
151
159
|
}
|
|
@@ -207,6 +215,111 @@ async function optimizeScreenshotBuffer(buffer, {
|
|
|
207
215
|
}
|
|
208
216
|
}
|
|
209
217
|
|
|
218
|
+
async function composeScreenshotsForLlm(screenshots = [], {
|
|
219
|
+
basePath,
|
|
220
|
+
pagesPerImage = 3,
|
|
221
|
+
resizeMaxWidth = 1100,
|
|
222
|
+
quality = 72
|
|
223
|
+
} = {}) {
|
|
224
|
+
const fileScreenshots = screenshots.filter((item) => item?.file_path);
|
|
225
|
+
if (!basePath || fileScreenshots.length <= 1) {
|
|
226
|
+
return {
|
|
227
|
+
llm_file_paths: fileScreenshots.map((item) => item.file_path),
|
|
228
|
+
llm_screenshots: [],
|
|
229
|
+
llm_total_byte_length: 0,
|
|
230
|
+
llm_original_total_byte_length: 0,
|
|
231
|
+
llm_composition_error: null
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const safePagesPerImage = Math.max(1, Math.min(5, Number(pagesPerImage) || 3));
|
|
236
|
+
const safeWidth = Math.max(700, Math.min(1400, Number(resizeMaxWidth) || 1100));
|
|
237
|
+
const safeQuality = Math.max(45, Math.min(90, Number(quality) || 72));
|
|
238
|
+
const llmScreenshots = [];
|
|
239
|
+
|
|
240
|
+
try {
|
|
241
|
+
for (let index = 0; index < fileScreenshots.length; index += safePagesPerImage) {
|
|
242
|
+
const group = fileScreenshots.slice(index, index + safePagesPerImage);
|
|
243
|
+
const prepared = [];
|
|
244
|
+
for (const item of group) {
|
|
245
|
+
const sourceBuffer = fs.readFileSync(item.file_path);
|
|
246
|
+
const { data, info } = await sharp(sourceBuffer, { failOn: "none" })
|
|
247
|
+
.resize({
|
|
248
|
+
width: safeWidth,
|
|
249
|
+
withoutEnlargement: true
|
|
250
|
+
})
|
|
251
|
+
.jpeg({
|
|
252
|
+
quality: safeQuality,
|
|
253
|
+
mozjpeg: true
|
|
254
|
+
})
|
|
255
|
+
.toBuffer({ resolveWithObject: true });
|
|
256
|
+
prepared.push({
|
|
257
|
+
input: data,
|
|
258
|
+
width: info.width,
|
|
259
|
+
height: info.height,
|
|
260
|
+
source_file_path: item.file_path
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const width = Math.max(...prepared.map((item) => item.width), 1);
|
|
265
|
+
const height = prepared.reduce((sum, item) => sum + item.height, 0);
|
|
266
|
+
let top = 0;
|
|
267
|
+
const composites = prepared.map((item) => {
|
|
268
|
+
const layer = {
|
|
269
|
+
input: item.input,
|
|
270
|
+
left: 0,
|
|
271
|
+
top
|
|
272
|
+
};
|
|
273
|
+
top += item.height;
|
|
274
|
+
return layer;
|
|
275
|
+
});
|
|
276
|
+
const outputBuffer = await sharp({
|
|
277
|
+
create: {
|
|
278
|
+
width,
|
|
279
|
+
height,
|
|
280
|
+
channels: 3,
|
|
281
|
+
background: "#ffffff"
|
|
282
|
+
}
|
|
283
|
+
})
|
|
284
|
+
.composite(composites)
|
|
285
|
+
.jpeg({
|
|
286
|
+
quality: safeQuality,
|
|
287
|
+
mozjpeg: true
|
|
288
|
+
})
|
|
289
|
+
.toBuffer();
|
|
290
|
+
const outputPath = filePathForLlmSequence(basePath, llmScreenshots.length);
|
|
291
|
+
fs.writeFileSync(outputPath, outputBuffer);
|
|
292
|
+
llmScreenshots.push({
|
|
293
|
+
index: llmScreenshots.length,
|
|
294
|
+
file_path: outputPath,
|
|
295
|
+
byte_length: outputBuffer.length,
|
|
296
|
+
source_file_paths: prepared.map((item) => item.source_file_path),
|
|
297
|
+
source_page_count: prepared.length,
|
|
298
|
+
width,
|
|
299
|
+
height,
|
|
300
|
+
format: "jpeg",
|
|
301
|
+
mime_type: "image/jpeg"
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
} catch (error) {
|
|
305
|
+
return {
|
|
306
|
+
llm_file_paths: fileScreenshots.map((item) => item.file_path),
|
|
307
|
+
llm_screenshots: [],
|
|
308
|
+
llm_total_byte_length: 0,
|
|
309
|
+
llm_original_total_byte_length: fileScreenshots.reduce((sum, item) => sum + (Number(item.byte_length) || 0), 0),
|
|
310
|
+
llm_composition_error: error?.message || String(error)
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
return {
|
|
315
|
+
llm_file_paths: llmScreenshots.map((item) => item.file_path),
|
|
316
|
+
llm_screenshots: llmScreenshots,
|
|
317
|
+
llm_total_byte_length: llmScreenshots.reduce((sum, item) => sum + (Number(item.byte_length) || 0), 0),
|
|
318
|
+
llm_original_total_byte_length: fileScreenshots.reduce((sum, item) => sum + (Number(item.byte_length) || 0), 0),
|
|
319
|
+
llm_composition_error: null
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
210
323
|
export async function captureScrolledNodeScreenshots(client, nodeId, {
|
|
211
324
|
filePath,
|
|
212
325
|
format = "png",
|
|
@@ -222,6 +335,10 @@ export async function captureScrolledNodeScreenshots(client, nodeId, {
|
|
|
222
335
|
skipDuplicateScreenshots = false,
|
|
223
336
|
optimize = false,
|
|
224
337
|
resizeMaxWidth = 0,
|
|
338
|
+
composeForLlm = false,
|
|
339
|
+
llmPagesPerImage = 3,
|
|
340
|
+
llmResizeMaxWidth = 1100,
|
|
341
|
+
llmQuality = 72,
|
|
225
342
|
metadata = {}
|
|
226
343
|
} = {}) {
|
|
227
344
|
if (!nodeId) throw new Error("captureScrolledNodeScreenshots requires nodeId");
|
|
@@ -322,6 +439,21 @@ export async function captureScrolledNodeScreenshots(client, nodeId, {
|
|
|
322
439
|
}
|
|
323
440
|
}
|
|
324
441
|
|
|
442
|
+
const llmComposition = composeForLlm
|
|
443
|
+
? await composeScreenshotsForLlm(screenshots, {
|
|
444
|
+
basePath: filePath,
|
|
445
|
+
pagesPerImage: llmPagesPerImage,
|
|
446
|
+
resizeMaxWidth: llmResizeMaxWidth,
|
|
447
|
+
quality: llmQuality
|
|
448
|
+
})
|
|
449
|
+
: {
|
|
450
|
+
llm_file_paths: screenshots.map((item) => item.file_path).filter(Boolean),
|
|
451
|
+
llm_screenshots: [],
|
|
452
|
+
llm_total_byte_length: 0,
|
|
453
|
+
llm_original_total_byte_length: 0,
|
|
454
|
+
llm_composition_error: null
|
|
455
|
+
};
|
|
456
|
+
|
|
325
457
|
return {
|
|
326
458
|
schema_version: 1,
|
|
327
459
|
source: "image-scroll-sequence",
|
|
@@ -335,12 +467,22 @@ export async function captureScrolledNodeScreenshots(client, nodeId, {
|
|
|
335
467
|
dropped_duplicate_count: droppedDuplicateCount,
|
|
336
468
|
total_byte_length: screenshots.reduce((sum, item) => sum + (Number(item.byte_length) || 0), 0),
|
|
337
469
|
original_total_byte_length: screenshots.reduce((sum, item) => sum + (Number(item.original_byte_length) || 0), 0),
|
|
470
|
+
llm_file_paths: llmComposition.llm_file_paths,
|
|
471
|
+
llm_screenshot_count: llmComposition.llm_file_paths.length,
|
|
472
|
+
llm_total_byte_length: llmComposition.llm_total_byte_length,
|
|
473
|
+
llm_original_total_byte_length: llmComposition.llm_original_total_byte_length,
|
|
474
|
+
llm_composition_error: llmComposition.llm_composition_error,
|
|
475
|
+
llm_screenshots: llmComposition.llm_screenshots,
|
|
338
476
|
optimization: {
|
|
339
477
|
enabled: Boolean(optimize),
|
|
340
478
|
resize_max_width: Math.max(0, Number(resizeMaxWidth) || 0),
|
|
341
479
|
capture_viewport: Boolean(captureViewport),
|
|
342
480
|
format,
|
|
343
|
-
quality: quality ?? null
|
|
481
|
+
quality: quality ?? null,
|
|
482
|
+
llm_compose_enabled: Boolean(composeForLlm),
|
|
483
|
+
llm_pages_per_image: Math.max(1, Math.min(5, Number(llmPagesPerImage) || 3)),
|
|
484
|
+
llm_resize_max_width: Math.max(0, Number(llmResizeMaxWidth) || 0),
|
|
485
|
+
llm_quality: llmQuality ?? null
|
|
344
486
|
},
|
|
345
487
|
file_paths: screenshots.map((item) => item.file_path).filter(Boolean),
|
|
346
488
|
screenshots,
|
|
@@ -132,8 +132,13 @@ export function summarizeImageEvidence(imageEvidence = null) {
|
|
|
132
132
|
dropped_duplicate_count: imageEvidence.dropped_duplicate_count || 0,
|
|
133
133
|
total_byte_length: imageEvidence.total_byte_length || 0,
|
|
134
134
|
original_total_byte_length: imageEvidence.original_total_byte_length || 0,
|
|
135
|
+
llm_screenshot_count: imageEvidence.llm_screenshot_count || 0,
|
|
136
|
+
llm_total_byte_length: imageEvidence.llm_total_byte_length || 0,
|
|
137
|
+
llm_original_total_byte_length: imageEvidence.llm_original_total_byte_length || 0,
|
|
138
|
+
llm_composition_error: imageEvidence.llm_composition_error || null,
|
|
135
139
|
optimization: imageEvidence.optimization || null,
|
|
136
140
|
file_paths: imageEvidence.file_paths || [],
|
|
141
|
+
llm_file_paths: imageEvidence.llm_file_paths || [],
|
|
137
142
|
first_clip: imageEvidence.screenshots?.[0]?.clip || imageEvidence.clip || null
|
|
138
143
|
};
|
|
139
144
|
}
|
|
@@ -392,11 +392,18 @@ function normalizeImagePaths({ imageEvidence = null, imagePaths = [] } = {}) {
|
|
|
392
392
|
if (Array.isArray(imagePaths)) {
|
|
393
393
|
paths.push(...imagePaths);
|
|
394
394
|
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
if (
|
|
399
|
-
paths.push(...
|
|
395
|
+
const evidenceLlmPaths = Array.isArray(imageEvidence?.llm_file_paths)
|
|
396
|
+
? imageEvidence.llm_file_paths
|
|
397
|
+
: [];
|
|
398
|
+
if (evidenceLlmPaths.length) {
|
|
399
|
+
paths.push(...evidenceLlmPaths);
|
|
400
|
+
} else {
|
|
401
|
+
if (Array.isArray(imageEvidence?.file_paths)) {
|
|
402
|
+
paths.push(...imageEvidence.file_paths);
|
|
403
|
+
}
|
|
404
|
+
if (Array.isArray(imageEvidence?.screenshots)) {
|
|
405
|
+
paths.push(...imageEvidence.screenshots.map((item) => item.file_path));
|
|
406
|
+
}
|
|
400
407
|
}
|
|
401
408
|
return unique(paths.map((filePath) => String(filePath || "").trim()).filter(Boolean));
|
|
402
409
|
}
|
|
@@ -1292,6 +1299,7 @@ export function compactScreeningLlmResult(llmResult) {
|
|
|
1292
1299
|
usage: llmResult.usage || null,
|
|
1293
1300
|
finish_reason: llmResult.finish_reason || null,
|
|
1294
1301
|
image_input_count: llmResult.image_input_count || 0,
|
|
1302
|
+
attempt_count: llmResult.attempt_count || 0,
|
|
1295
1303
|
error: llmResult.error || null,
|
|
1296
1304
|
screened_at: llmResult.screened_at || null
|
|
1297
1305
|
};
|
|
@@ -1324,6 +1332,7 @@ export function createFailedLlmScreeningResult(error) {
|
|
|
1324
1332
|
raw_model_output: "",
|
|
1325
1333
|
image_input_count: Number(error?.image_input_count) || 0,
|
|
1326
1334
|
image_inputs: Array.isArray(error?.image_inputs) ? error.image_inputs : [],
|
|
1335
|
+
attempt_count: Number(error?.llm_attempt_count) || 0,
|
|
1327
1336
|
error: error?.message || String(error || "unknown"),
|
|
1328
1337
|
screened_at: nowIso()
|
|
1329
1338
|
};
|
|
@@ -1352,7 +1361,7 @@ export function buildScreeningLlmMessages({
|
|
|
1352
1361
|
`请根据以下标准判断候选人是否通过筛选。\n\n筛选标准:\n${safeCriteria}\n\n`
|
|
1353
1362
|
+ `候选人信息:\n${safeText || "候选人的完整简历信息在后续截图中,请按截图顺序阅读。"}\n\n`
|
|
1354
1363
|
+ (images.length
|
|
1355
|
-
? `候选人简历截图共 ${images.length}
|
|
1364
|
+
? `候选人简历截图共 ${images.length} 张,按从上到下的滚动顺序排列。若截图是拼接长图,请按图内从上到下顺序完整阅读;不要跳过任何一段简历内容。\n\n`
|
|
1356
1365
|
: "")
|
|
1357
1366
|
+ "要求:\n"
|
|
1358
1367
|
+ "1) 只能依据候选人信息或截图中真实出现的内容判断。\n"
|
|
@@ -1383,6 +1392,24 @@ export function buildScreeningLlmMessages({
|
|
|
1383
1392
|
];
|
|
1384
1393
|
}
|
|
1385
1394
|
|
|
1395
|
+
function normalizeLlmMaxRetries(value) {
|
|
1396
|
+
if (value == null || value === "") return 1;
|
|
1397
|
+
const parsed = Number(value);
|
|
1398
|
+
if (!Number.isFinite(parsed) || parsed < 0) return 1;
|
|
1399
|
+
return Math.min(3, Math.floor(parsed));
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
function isRetryableLlmRequestError(error) {
|
|
1403
|
+
const status = Number(error?.status);
|
|
1404
|
+
if ([408, 409, 425, 429].includes(status) || status >= 500) return true;
|
|
1405
|
+
return /(?:aborted|abort|timeout|timed out|fetch failed|socket|network|ECONNRESET|ETIMEDOUT|EAI_AGAIN)/i
|
|
1406
|
+
.test(String(error?.message || error || ""));
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
function sleepMs(ms) {
|
|
1410
|
+
return new Promise((resolve) => setTimeout(resolve, Math.max(0, Number(ms) || 0)));
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1386
1413
|
export async function callScreeningLlm({
|
|
1387
1414
|
candidate,
|
|
1388
1415
|
criteria,
|
|
@@ -1422,12 +1449,16 @@ export async function callScreeningLlm({
|
|
|
1422
1449
|
applyChatCompletionThinking(payload, {
|
|
1423
1450
|
baseUrl,
|
|
1424
1451
|
model,
|
|
1425
|
-
thinkingLevel: config.llmThinkingLevel || config.thinkingLevel || config.reasoningEffort || "
|
|
1452
|
+
thinkingLevel: config.llmThinkingLevel || config.thinkingLevel || config.reasoningEffort || "low"
|
|
1426
1453
|
});
|
|
1427
1454
|
|
|
1428
|
-
const
|
|
1429
|
-
const
|
|
1430
|
-
|
|
1455
|
+
const maxRetries = normalizeLlmMaxRetries(config.llmMaxRetries ?? config.maxRetries);
|
|
1456
|
+
const maxAttempts = maxRetries + 1;
|
|
1457
|
+
let lastError = null;
|
|
1458
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
1459
|
+
const controller = new AbortController();
|
|
1460
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
1461
|
+
try {
|
|
1431
1462
|
const headers = {
|
|
1432
1463
|
"Content-Type": "application/json",
|
|
1433
1464
|
Authorization: `Bearer ${apiKey}`
|
|
@@ -1443,7 +1474,9 @@ export async function callScreeningLlm({
|
|
|
1443
1474
|
});
|
|
1444
1475
|
const responseText = await response.text();
|
|
1445
1476
|
if (!response.ok) {
|
|
1446
|
-
|
|
1477
|
+
const error = new Error(`LLM request failed: ${response.status} ${responseText.slice(0, 400)}`);
|
|
1478
|
+
error.status = response.status;
|
|
1479
|
+
throw error;
|
|
1447
1480
|
}
|
|
1448
1481
|
const json = tryParseJson(responseText);
|
|
1449
1482
|
if (!json) {
|
|
@@ -1485,13 +1518,25 @@ export async function callScreeningLlm({
|
|
|
1485
1518
|
raw_content_length: content.length,
|
|
1486
1519
|
image_input_count: imageInputs.length,
|
|
1487
1520
|
image_inputs: summarizeLlmImageInputs(imageInputs),
|
|
1521
|
+
attempt_count: attempt,
|
|
1488
1522
|
screened_at: nowIso()
|
|
1489
1523
|
};
|
|
1490
1524
|
} catch (error) {
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1525
|
+
lastError = error;
|
|
1526
|
+
if (attempt >= maxAttempts || !isRetryableLlmRequestError(error)) {
|
|
1527
|
+
error.image_input_count = imageInputs.length;
|
|
1528
|
+
error.image_inputs = summarizeLlmImageInputs(imageInputs);
|
|
1529
|
+
error.llm_attempt_count = attempt;
|
|
1530
|
+
throw error;
|
|
1531
|
+
}
|
|
1532
|
+
await sleepMs(Math.min(2500, 500 * attempt));
|
|
1494
1533
|
} finally {
|
|
1495
1534
|
clearTimeout(timer);
|
|
1496
1535
|
}
|
|
1536
|
+
}
|
|
1537
|
+
lastError = lastError || new Error("LLM request failed without response");
|
|
1538
|
+
lastError.image_input_count = imageInputs.length;
|
|
1539
|
+
lastError.image_inputs = summarizeLlmImageInputs(imageInputs);
|
|
1540
|
+
lastError.llm_attempt_count = maxAttempts;
|
|
1541
|
+
throw lastError;
|
|
1497
1542
|
}
|
|
@@ -98,6 +98,7 @@ function compactLlmResult(llmResult) {
|
|
|
98
98
|
usage: llmResult.usage || null,
|
|
99
99
|
finish_reason: llmResult.finish_reason || null,
|
|
100
100
|
image_input_count: llmResult.image_input_count || 0,
|
|
101
|
+
attempt_count: llmResult.attempt_count || 0,
|
|
101
102
|
error: llmResult.error || null
|
|
102
103
|
};
|
|
103
104
|
}
|
|
@@ -826,11 +827,15 @@ export async function runChatWorkflow({
|
|
|
826
827
|
maxScreenshots: maxImagePages,
|
|
827
828
|
wheelDeltaY: imageWheelDeltaY,
|
|
828
829
|
settleMs: 350,
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
830
|
+
duplicateStopCount: 1,
|
|
831
|
+
skipDuplicateScreenshots: true,
|
|
832
|
+
composeForLlm: true,
|
|
833
|
+
llmPagesPerImage: 3,
|
|
834
|
+
llmResizeMaxWidth: 1100,
|
|
835
|
+
llmQuality: 72,
|
|
836
|
+
metadata: {
|
|
837
|
+
domain: "chat",
|
|
838
|
+
capture_mode: "scroll_sequence",
|
|
834
839
|
acquisition_reason: normalizedDetailSource === "image"
|
|
835
840
|
? "forced_image"
|
|
836
841
|
: "network_miss_image_fallback",
|
|
@@ -110,95 +110,117 @@ export async function refreshRecommendListAtEnd(client, {
|
|
|
110
110
|
);
|
|
111
111
|
attempts.push(buttonResult);
|
|
112
112
|
if (buttonResult.ok) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
113
|
+
try {
|
|
114
|
+
currentRootState = await getRecommendRoots(client);
|
|
115
|
+
const pageScopeResult = await selectRecommendPageScope(
|
|
116
|
+
client,
|
|
117
|
+
currentRootState.iframe.documentNodeId,
|
|
118
|
+
{
|
|
119
|
+
pageScope,
|
|
120
|
+
fallbackScope: fallbackPageScope,
|
|
121
|
+
settleMs: buttonSettleMs > 10000 ? 3000 : 1200,
|
|
122
|
+
timeoutMs: Math.max(10000, Math.min(cardTimeoutMs, 60000))
|
|
123
|
+
}
|
|
124
|
+
);
|
|
125
|
+
if (!pageScopeResult.selected) {
|
|
126
|
+
throw new Error(`Recommend page scope was not selected after end refresh: ${pageScopeResult.reason || pageScope}`);
|
|
122
127
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
128
|
+
currentRootState = await getRecommendRoots(client);
|
|
129
|
+
const filterResult = await selectAndConfirmFirstSafeFilter(
|
|
130
|
+
client,
|
|
131
|
+
currentRootState.iframe.documentNodeId,
|
|
132
|
+
buildRecommendFilterSelectionOptions(filter, { forceRecentNotView })
|
|
133
|
+
);
|
|
134
|
+
const cardNodeIds = await waitForRecommendCardNodeIds(client, currentRootState.iframe.documentNodeId, {
|
|
135
|
+
timeoutMs: cardTimeoutMs,
|
|
136
|
+
intervalMs: 500
|
|
137
|
+
});
|
|
138
|
+
return {
|
|
139
|
+
ok: cardNodeIds.length > 0,
|
|
140
|
+
method: "end_refresh_button",
|
|
141
|
+
attempts,
|
|
142
|
+
page_scope: pageScopeResult,
|
|
143
|
+
filter: filterResult,
|
|
144
|
+
card_count: cardNodeIds.length,
|
|
145
|
+
root_state: currentRootState,
|
|
146
|
+
forced_recent_not_view: forceRecentNotView
|
|
147
|
+
};
|
|
148
|
+
} catch (error) {
|
|
149
|
+
attempts.push({
|
|
150
|
+
ok: false,
|
|
151
|
+
method: "end_refresh_button_after_click",
|
|
152
|
+
reason: "end_refresh_reapply_failed",
|
|
153
|
+
error: error?.message || String(error)
|
|
154
|
+
});
|
|
126
155
|
}
|
|
127
|
-
currentRootState = await getRecommendRoots(client);
|
|
128
|
-
const filterResult = await selectAndConfirmFirstSafeFilter(
|
|
129
|
-
client,
|
|
130
|
-
currentRootState.iframe.documentNodeId,
|
|
131
|
-
buildRecommendFilterSelectionOptions(filter, { forceRecentNotView })
|
|
132
|
-
);
|
|
133
|
-
const cardNodeIds = await waitForRecommendCardNodeIds(client, currentRootState.iframe.documentNodeId, {
|
|
134
|
-
timeoutMs: cardTimeoutMs,
|
|
135
|
-
intervalMs: 500
|
|
136
|
-
});
|
|
137
|
-
return {
|
|
138
|
-
ok: cardNodeIds.length > 0,
|
|
139
|
-
method: "end_refresh_button",
|
|
140
|
-
attempts,
|
|
141
|
-
page_scope: pageScopeResult,
|
|
142
|
-
filter: filterResult,
|
|
143
|
-
card_count: cardNodeIds.length,
|
|
144
|
-
root_state: currentRootState,
|
|
145
|
-
forced_recent_not_view: forceRecentNotView
|
|
146
|
-
};
|
|
147
156
|
}
|
|
148
157
|
}
|
|
149
158
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
if (!currentRootState?.iframe?.documentNodeId) {
|
|
157
|
-
throw new Error("Recommend iframe was not ready after refresh reload");
|
|
158
|
-
}
|
|
159
|
-
let jobSelection = null;
|
|
160
|
-
if (jobLabel) {
|
|
161
|
-
jobSelection = await selectRecommendJob(client, currentRootState.iframe.documentNodeId, {
|
|
162
|
-
jobLabel,
|
|
163
|
-
settleMs: reloadSettleMs > 10000 ? 12000 : 6000
|
|
159
|
+
try {
|
|
160
|
+
await client.Page.reload({ ignoreCache: true });
|
|
161
|
+
if (reloadSettleMs > 0) await sleep(reloadSettleMs);
|
|
162
|
+
currentRootState = await waitForRecommendRoots(client, {
|
|
163
|
+
timeoutMs: Math.max(30000, reloadSettleMs * 4),
|
|
164
|
+
intervalMs: 500
|
|
164
165
|
});
|
|
165
|
-
if (!
|
|
166
|
-
throw new Error(
|
|
166
|
+
if (!currentRootState?.iframe?.documentNodeId) {
|
|
167
|
+
throw new Error("Recommend iframe was not ready after refresh reload");
|
|
167
168
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
169
|
+
let jobSelection = null;
|
|
170
|
+
if (jobLabel) {
|
|
171
|
+
jobSelection = await selectRecommendJob(client, currentRootState.iframe.documentNodeId, {
|
|
172
|
+
jobLabel,
|
|
173
|
+
settleMs: reloadSettleMs > 10000 ? 12000 : 6000
|
|
174
|
+
});
|
|
175
|
+
if (!jobSelection.selected) {
|
|
176
|
+
throw new Error(`Requested recommend job was not selected after refresh reload: ${jobSelection.reason}`);
|
|
177
|
+
}
|
|
178
|
+
currentRootState = await getRecommendRoots(client);
|
|
178
179
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
180
|
+
const pageScopeResult = await selectRecommendPageScope(
|
|
181
|
+
client,
|
|
182
|
+
currentRootState.iframe.documentNodeId,
|
|
183
|
+
{
|
|
184
|
+
pageScope,
|
|
185
|
+
fallbackScope: fallbackPageScope,
|
|
186
|
+
settleMs: reloadSettleMs > 10000 ? 3000 : 1200,
|
|
187
|
+
timeoutMs: Math.max(10000, Math.min(cardTimeoutMs, 60000))
|
|
188
|
+
}
|
|
189
|
+
);
|
|
190
|
+
if (!pageScopeResult.selected) {
|
|
191
|
+
throw new Error(`Recommend page scope was not selected after refresh reload: ${pageScopeResult.reason || pageScope}`);
|
|
192
|
+
}
|
|
193
|
+
currentRootState = await getRecommendRoots(client);
|
|
194
|
+
const filterResult = await selectAndConfirmFirstSafeFilter(
|
|
195
|
+
client,
|
|
196
|
+
currentRootState.iframe.documentNodeId,
|
|
197
|
+
buildRecommendFilterSelectionOptions(filter, { forceRecentNotView })
|
|
198
|
+
);
|
|
199
|
+
const cardNodeIds = await waitForRecommendCardNodeIds(client, currentRootState.iframe.documentNodeId, {
|
|
200
|
+
timeoutMs: cardTimeoutMs,
|
|
201
|
+
intervalMs: 500
|
|
202
|
+
});
|
|
203
|
+
return {
|
|
204
|
+
ok: cardNodeIds.length > 0,
|
|
205
|
+
method: "page_reload",
|
|
206
|
+
attempts,
|
|
207
|
+
job_selection: jobSelection,
|
|
208
|
+
page_scope: pageScopeResult,
|
|
209
|
+
filter: filterResult,
|
|
210
|
+
card_count: cardNodeIds.length,
|
|
211
|
+
root_state: currentRootState,
|
|
212
|
+
forced_recent_not_view: forceRecentNotView
|
|
213
|
+
};
|
|
214
|
+
} catch (error) {
|
|
215
|
+
return {
|
|
216
|
+
ok: false,
|
|
217
|
+
method: "page_reload",
|
|
218
|
+
reason: "page_reload_failed",
|
|
219
|
+
error: error?.message || String(error),
|
|
220
|
+
attempts,
|
|
221
|
+
card_count: 0,
|
|
222
|
+
root_state: currentRootState,
|
|
223
|
+
forced_recent_not_view: forceRecentNotView
|
|
224
|
+
};
|
|
182
225
|
}
|
|
183
|
-
currentRootState = await getRecommendRoots(client);
|
|
184
|
-
const filterResult = await selectAndConfirmFirstSafeFilter(
|
|
185
|
-
client,
|
|
186
|
-
currentRootState.iframe.documentNodeId,
|
|
187
|
-
buildRecommendFilterSelectionOptions(filter, { forceRecentNotView })
|
|
188
|
-
);
|
|
189
|
-
const cardNodeIds = await waitForRecommendCardNodeIds(client, currentRootState.iframe.documentNodeId, {
|
|
190
|
-
timeoutMs: cardTimeoutMs,
|
|
191
|
-
intervalMs: 500
|
|
192
|
-
});
|
|
193
|
-
return {
|
|
194
|
-
ok: cardNodeIds.length > 0,
|
|
195
|
-
method: "page_reload",
|
|
196
|
-
attempts,
|
|
197
|
-
job_selection: jobSelection,
|
|
198
|
-
page_scope: pageScopeResult,
|
|
199
|
-
filter: filterResult,
|
|
200
|
-
card_count: cardNodeIds.length,
|
|
201
|
-
root_state: currentRootState,
|
|
202
|
-
forced_recent_not_view: forceRecentNotView
|
|
203
|
-
};
|
|
204
226
|
}
|
|
@@ -715,6 +715,10 @@ export async function runRecommendWorkflow({
|
|
|
715
715
|
settleMs: 350,
|
|
716
716
|
duplicateStopCount: 1,
|
|
717
717
|
skipDuplicateScreenshots: true,
|
|
718
|
+
composeForLlm: true,
|
|
719
|
+
llmPagesPerImage: 3,
|
|
720
|
+
llmResizeMaxWidth: 1100,
|
|
721
|
+
llmQuality: 72,
|
|
718
722
|
metadata: {
|
|
719
723
|
domain: "recommend",
|
|
720
724
|
capture_mode: "scroll_sequence",
|
|
@@ -440,6 +440,10 @@ export async function runRecruitWorkflow({
|
|
|
440
440
|
settleMs: 350,
|
|
441
441
|
duplicateStopCount: 1,
|
|
442
442
|
skipDuplicateScreenshots: true,
|
|
443
|
+
composeForLlm: true,
|
|
444
|
+
llmPagesPerImage: 3,
|
|
445
|
+
llmResizeMaxWidth: 1100,
|
|
446
|
+
llmQuality: 72,
|
|
443
447
|
metadata: {
|
|
444
448
|
domain: "recruit",
|
|
445
449
|
capture_mode: "scroll_sequence",
|