@reconcrap/boss-recommend-mcp 2.0.5 → 2.0.7
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 +2 -0
- package/package.json +1 -1
- package/src/chat-mcp.js +59 -18
- package/src/core/reporting/legacy-csv.js +2 -2
- package/src/core/screening/index.js +48 -0
- package/src/domains/chat/run-service.js +93 -40
- package/src/domains/recommend/detail.js +189 -6
- package/src/domains/recommend/run-service.js +92 -9
- package/src/domains/recruit/run-service.js +87 -12
- package/src/index.js +50 -5
- package/src/recommend-mcp.js +81 -7
- package/src/recruit-mcp.js +103 -8
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
querySelectorAll,
|
|
9
9
|
sleep
|
|
10
10
|
} from "../../core/browser/index.js";
|
|
11
|
+
import { candidateKeyFromProfile } from "../../core/infinite-list/index.js";
|
|
11
12
|
import {
|
|
12
13
|
buildScreeningCandidateFromDetail,
|
|
13
14
|
htmlToText
|
|
@@ -22,6 +23,10 @@ import {
|
|
|
22
23
|
getRecommendRoots,
|
|
23
24
|
queryFirstAcrossRoots
|
|
24
25
|
} from "./roots.js";
|
|
26
|
+
import {
|
|
27
|
+
findRecommendCardNodeIds,
|
|
28
|
+
readRecommendCardCandidate
|
|
29
|
+
} from "./cards.js";
|
|
25
30
|
|
|
26
31
|
export function matchesRecommendDetailNetwork(url) {
|
|
27
32
|
return DETAIL_NETWORK_PATTERNS.some((pattern) => pattern.test(String(url || "")));
|
|
@@ -146,14 +151,36 @@ export async function readRecommendDetailHtml(client, detailState) {
|
|
|
146
151
|
let popupHTML = "";
|
|
147
152
|
let resumeHTML = "";
|
|
148
153
|
let resumeIframeDocumentNodeId = null;
|
|
154
|
+
const errors = [];
|
|
149
155
|
|
|
150
156
|
if (detailState?.popup?.node_id) {
|
|
151
|
-
|
|
157
|
+
try {
|
|
158
|
+
popupHTML = await getOuterHTML(client, detailState.popup.node_id);
|
|
159
|
+
} catch (error) {
|
|
160
|
+
errors.push({
|
|
161
|
+
source: "popup",
|
|
162
|
+
node_id: detailState.popup.node_id,
|
|
163
|
+
stale_node: isStaleRecommendNodeError(error),
|
|
164
|
+
error: error?.message || String(error)
|
|
165
|
+
});
|
|
166
|
+
}
|
|
152
167
|
}
|
|
153
168
|
|
|
154
169
|
if (detailState?.resumeIframe?.node_id) {
|
|
155
|
-
|
|
156
|
-
|
|
170
|
+
try {
|
|
171
|
+
resumeIframeDocumentNodeId = await getFrameDocumentNodeId(client, detailState.resumeIframe.node_id);
|
|
172
|
+
resumeHTML = await getOuterHTML(client, resumeIframeDocumentNodeId);
|
|
173
|
+
} catch (error) {
|
|
174
|
+
errors.push({
|
|
175
|
+
source: "resume_iframe",
|
|
176
|
+
node_id: detailState.resumeIframe.node_id,
|
|
177
|
+
document_node_id: resumeIframeDocumentNodeId,
|
|
178
|
+
stale_node: isStaleRecommendNodeError(error),
|
|
179
|
+
error: error?.message || String(error)
|
|
180
|
+
});
|
|
181
|
+
resumeIframeDocumentNodeId = null;
|
|
182
|
+
resumeHTML = "";
|
|
183
|
+
}
|
|
157
184
|
}
|
|
158
185
|
|
|
159
186
|
return {
|
|
@@ -161,7 +188,90 @@ export async function readRecommendDetailHtml(client, detailState) {
|
|
|
161
188
|
resumeHTML,
|
|
162
189
|
resumeIframeDocumentNodeId,
|
|
163
190
|
popupText: htmlToText(popupHTML),
|
|
164
|
-
resumeText: htmlToText(resumeHTML)
|
|
191
|
+
resumeText: htmlToText(resumeHTML),
|
|
192
|
+
errors
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export function isStaleRecommendNodeError(error) {
|
|
197
|
+
const message = String(error?.message || error || "");
|
|
198
|
+
return /Could not find node with given id|No node with given id|Node is detached|Cannot find node/i.test(message);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export async function findRecommendCardNodeForCandidateKey(client, {
|
|
202
|
+
candidateKey = "",
|
|
203
|
+
rootState = null,
|
|
204
|
+
targetUrl = "",
|
|
205
|
+
source = "recommend-run-card-retry",
|
|
206
|
+
timeoutMs = 5000,
|
|
207
|
+
intervalMs = 250
|
|
208
|
+
} = {}) {
|
|
209
|
+
if (!candidateKey) {
|
|
210
|
+
return {
|
|
211
|
+
ok: false,
|
|
212
|
+
reason: "candidate_key_required"
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const started = Date.now();
|
|
217
|
+
let lastError = null;
|
|
218
|
+
let lastCardCount = 0;
|
|
219
|
+
while (Date.now() - started <= timeoutMs) {
|
|
220
|
+
const currentRootState = rootState?.iframe?.documentNodeId
|
|
221
|
+
? rootState
|
|
222
|
+
: await getRecommendRoots(client);
|
|
223
|
+
const frameNodeId = currentRootState?.iframe?.documentNodeId;
|
|
224
|
+
if (!frameNodeId) {
|
|
225
|
+
return {
|
|
226
|
+
ok: false,
|
|
227
|
+
reason: "recommend_frame_not_found"
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const nodeIds = await findRecommendCardNodeIds(client, frameNodeId);
|
|
232
|
+
lastCardCount = nodeIds.length;
|
|
233
|
+
for (let visibleIndex = 0; visibleIndex < nodeIds.length; visibleIndex += 1) {
|
|
234
|
+
const nodeId = nodeIds[visibleIndex];
|
|
235
|
+
try {
|
|
236
|
+
const candidate = await readRecommendCardCandidate(client, nodeId, {
|
|
237
|
+
targetUrl,
|
|
238
|
+
source,
|
|
239
|
+
metadata: {
|
|
240
|
+
visible_index: visibleIndex,
|
|
241
|
+
retry_reason: "stale_detail_node"
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
const key = candidateKeyFromProfile(candidate, {
|
|
245
|
+
nodeId,
|
|
246
|
+
visibleIndex,
|
|
247
|
+
attributes: candidate?.attributes || candidate?.metadata?.attributes || {}
|
|
248
|
+
});
|
|
249
|
+
if (key === candidateKey) {
|
|
250
|
+
return {
|
|
251
|
+
ok: true,
|
|
252
|
+
node_id: nodeId,
|
|
253
|
+
visible_index: visibleIndex,
|
|
254
|
+
candidate,
|
|
255
|
+
key,
|
|
256
|
+
root_state: currentRootState,
|
|
257
|
+
card_count: nodeIds.length
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
} catch (error) {
|
|
261
|
+
lastError = error;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (intervalMs > 0) await sleep(intervalMs);
|
|
266
|
+
rootState = null;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return {
|
|
270
|
+
ok: false,
|
|
271
|
+
reason: "candidate_key_not_mounted",
|
|
272
|
+
candidate_key: candidateKey,
|
|
273
|
+
last_card_count: lastCardCount,
|
|
274
|
+
error: lastError?.message || null
|
|
165
275
|
};
|
|
166
276
|
}
|
|
167
277
|
|
|
@@ -181,6 +291,77 @@ export async function openRecommendCardDetail(client, cardNodeId, {
|
|
|
181
291
|
};
|
|
182
292
|
}
|
|
183
293
|
|
|
294
|
+
export async function openRecommendCardDetailWithFreshRetry(client, {
|
|
295
|
+
cardNodeId,
|
|
296
|
+
candidateKey = "",
|
|
297
|
+
cardCandidate = null,
|
|
298
|
+
rootState = null,
|
|
299
|
+
targetUrl = "",
|
|
300
|
+
timeoutMs = 12000,
|
|
301
|
+
scrollIntoView = true,
|
|
302
|
+
retryTimeoutMs = 5000,
|
|
303
|
+
retryIntervalMs = 250,
|
|
304
|
+
maxAttempts = 2
|
|
305
|
+
} = {}) {
|
|
306
|
+
let currentNodeId = cardNodeId;
|
|
307
|
+
let currentCandidate = cardCandidate;
|
|
308
|
+
let currentRootState = rootState;
|
|
309
|
+
const attempts = [];
|
|
310
|
+
const limit = Math.max(1, Number(maxAttempts) || 1);
|
|
311
|
+
|
|
312
|
+
for (let attemptIndex = 0; attemptIndex < limit; attemptIndex += 1) {
|
|
313
|
+
try {
|
|
314
|
+
const opened = await openRecommendCardDetail(client, currentNodeId, {
|
|
315
|
+
timeoutMs,
|
|
316
|
+
scrollIntoView
|
|
317
|
+
});
|
|
318
|
+
return {
|
|
319
|
+
...opened,
|
|
320
|
+
card_node_id: currentNodeId,
|
|
321
|
+
card_candidate: currentCandidate,
|
|
322
|
+
retry_attempts: attempts
|
|
323
|
+
};
|
|
324
|
+
} catch (error) {
|
|
325
|
+
const stale = isStaleRecommendNodeError(error);
|
|
326
|
+
attempts.push({
|
|
327
|
+
attempt: attemptIndex + 1,
|
|
328
|
+
node_id: currentNodeId,
|
|
329
|
+
stale_node: stale,
|
|
330
|
+
error: error?.message || String(error)
|
|
331
|
+
});
|
|
332
|
+
if (!stale || attemptIndex >= limit - 1 || !candidateKey) {
|
|
333
|
+
error.recommend_detail_open_attempts = attempts;
|
|
334
|
+
throw error;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const resolved = await findRecommendCardNodeForCandidateKey(client, {
|
|
338
|
+
candidateKey,
|
|
339
|
+
rootState: currentRootState,
|
|
340
|
+
targetUrl,
|
|
341
|
+
timeoutMs: retryTimeoutMs,
|
|
342
|
+
intervalMs: retryIntervalMs
|
|
343
|
+
});
|
|
344
|
+
attempts[attempts.length - 1].refresh_lookup = {
|
|
345
|
+
ok: Boolean(resolved.ok),
|
|
346
|
+
node_id: resolved.node_id || null,
|
|
347
|
+
visible_index: resolved.visible_index ?? null,
|
|
348
|
+
card_count: resolved.card_count || resolved.last_card_count || 0,
|
|
349
|
+
reason: resolved.reason || null,
|
|
350
|
+
error: resolved.error || null
|
|
351
|
+
};
|
|
352
|
+
if (!resolved.ok || !resolved.node_id) {
|
|
353
|
+
error.recommend_detail_open_attempts = attempts;
|
|
354
|
+
throw error;
|
|
355
|
+
}
|
|
356
|
+
currentNodeId = resolved.node_id;
|
|
357
|
+
currentCandidate = resolved.candidate || currentCandidate;
|
|
358
|
+
currentRootState = resolved.root_state || null;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
throw new Error("Recommend detail retry exhausted");
|
|
363
|
+
}
|
|
364
|
+
|
|
184
365
|
export async function closeRecommendDetail(client, {
|
|
185
366
|
attemptsLimit = 3
|
|
186
367
|
} = {}) {
|
|
@@ -317,7 +498,8 @@ export async function extractRecommendDetailCandidate(client, {
|
|
|
317
498
|
detail_popup_root: detailState?.popup?.root || null,
|
|
318
499
|
resume_iframe_selector: detailState?.resumeIframe?.selector || null,
|
|
319
500
|
resume_iframe_root: detailState?.resumeIframe?.root || null,
|
|
320
|
-
resume_iframe_document_node_id: detailHtml.resumeIframeDocumentNodeId
|
|
501
|
+
resume_iframe_document_node_id: detailHtml.resumeIframeDocumentNodeId,
|
|
502
|
+
detail_html_errors: detailHtml.errors || []
|
|
321
503
|
}
|
|
322
504
|
});
|
|
323
505
|
|
|
@@ -334,7 +516,8 @@ export async function extractRecommendDetailCandidate(client, {
|
|
|
334
516
|
popup_text: detailHtml.popupText,
|
|
335
517
|
resume_text: detailHtml.resumeText,
|
|
336
518
|
popup_html_length: detailHtml.popupHTML.length,
|
|
337
|
-
resume_html_length: detailHtml.resumeHTML.length
|
|
519
|
+
resume_html_length: detailHtml.resumeHTML.length,
|
|
520
|
+
html_errors: detailHtml.errors || []
|
|
338
521
|
},
|
|
339
522
|
close_result: closeResult
|
|
340
523
|
};
|
|
@@ -21,12 +21,18 @@ import {
|
|
|
21
21
|
resetInfiniteListForRefreshRound
|
|
22
22
|
} from "../../core/infinite-list/index.js";
|
|
23
23
|
import { createViewportRunGuard } from "../../core/self-heal/index.js";
|
|
24
|
-
import {
|
|
24
|
+
import {
|
|
25
|
+
callScreeningLlm,
|
|
26
|
+
compactScreeningLlmResult,
|
|
27
|
+
createFailedLlmScreeningResult,
|
|
28
|
+
llmResultToScreening,
|
|
29
|
+
screenCandidate
|
|
30
|
+
} from "../../core/screening/index.js";
|
|
25
31
|
import {
|
|
26
32
|
closeRecommendDetail,
|
|
27
33
|
createRecommendDetailNetworkRecorder,
|
|
28
34
|
extractRecommendDetailCandidate,
|
|
29
|
-
|
|
35
|
+
openRecommendCardDetailWithFreshRetry,
|
|
30
36
|
waitForRecommendDetailNetworkEvents
|
|
31
37
|
} from "./detail.js";
|
|
32
38
|
import {
|
|
@@ -165,10 +171,22 @@ function compactDetail(detailResult) {
|
|
|
165
171
|
parsed_network_profile_count: detailResult.parsed_network_profiles?.filter((item) => item.ok).length || 0,
|
|
166
172
|
cv_acquisition: detailResult.cv_acquisition || null,
|
|
167
173
|
image_evidence: summarizeImageEvidence(detailResult.image_evidence),
|
|
174
|
+
llm_screening: compactScreeningLlmResult(detailResult.llm_result),
|
|
168
175
|
close_result: detailResult.close_result
|
|
169
176
|
};
|
|
170
177
|
}
|
|
171
178
|
|
|
179
|
+
function normalizeScreeningMode(value) {
|
|
180
|
+
const normalized = String(value || "llm").trim().toLowerCase();
|
|
181
|
+
return ["deterministic", "local", "local_scorer"].includes(normalized)
|
|
182
|
+
? "deterministic"
|
|
183
|
+
: "llm";
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function createMissingLlmConfigResult() {
|
|
187
|
+
return createFailedLlmScreeningResult(new Error("LLM screening config is required for production recommend runs"));
|
|
188
|
+
}
|
|
189
|
+
|
|
172
190
|
function compactActionDiscovery(discovery) {
|
|
173
191
|
if (!discovery) return null;
|
|
174
192
|
return {
|
|
@@ -354,13 +372,20 @@ export async function runRecommendWorkflow({
|
|
|
354
372
|
executePostAction = true,
|
|
355
373
|
actionTimeoutMs = 8000,
|
|
356
374
|
actionIntervalMs = 500,
|
|
357
|
-
actionAfterClickDelayMs = 900
|
|
375
|
+
actionAfterClickDelayMs = 900,
|
|
376
|
+
screeningMode = "llm",
|
|
377
|
+
llmConfig = null,
|
|
378
|
+
llmTimeoutMs = 120000,
|
|
379
|
+
llmImageLimit = 8,
|
|
380
|
+
llmImageDetail = "high"
|
|
358
381
|
} = {}, runControl) {
|
|
359
382
|
if (!client) throw new Error("runRecommendWorkflow requires a guarded CDP client");
|
|
360
383
|
const normalizedFilter = normalizeFilter(filter);
|
|
361
384
|
const normalizedPostAction = normalizeRecommendPostAction(postAction) || "none";
|
|
362
385
|
const requestedPageScope = normalizeRecommendPageScope(pageScope) || "recommend";
|
|
363
386
|
const normalizedFallbackPageScope = normalizeRecommendPageScope(fallbackPageScope) || "recommend";
|
|
387
|
+
const normalizedScreeningMode = normalizeScreeningMode(screeningMode);
|
|
388
|
+
const useLlmScreening = normalizedScreeningMode !== "deterministic";
|
|
364
389
|
const postActionEnabled = normalizedPostAction !== "none";
|
|
365
390
|
const limit = Math.max(1, Number(maxCandidates) || 1);
|
|
366
391
|
const detailCountLimit = detailLimit == null ? limit : Math.max(0, Number(detailLimit) || 0);
|
|
@@ -484,6 +509,8 @@ export async function runRecommendWorkflow({
|
|
|
484
509
|
passed: 0,
|
|
485
510
|
greet_count: 0,
|
|
486
511
|
post_action_clicked: 0,
|
|
512
|
+
screening_mode: normalizedScreeningMode,
|
|
513
|
+
llm_screened: 0,
|
|
487
514
|
unique_seen: compactInfiniteListState(listState).seen_count,
|
|
488
515
|
scroll_count: 0,
|
|
489
516
|
refresh_rounds: 0,
|
|
@@ -562,6 +589,7 @@ export async function runRecommendWorkflow({
|
|
|
562
589
|
screened: results.length,
|
|
563
590
|
detail_opened: results.filter((item) => item.detail).length,
|
|
564
591
|
passed: results.filter((item) => item.screening.passed).length,
|
|
592
|
+
llm_screened: results.filter((item) => item.detail?.llm_screening || item.llm_screening).length,
|
|
565
593
|
unique_seen: compactInfiniteListState(listState).seen_count,
|
|
566
594
|
scroll_count: compactInfiniteListState(listState).scroll_count,
|
|
567
595
|
refresh_rounds: refreshRounds,
|
|
@@ -596,9 +624,9 @@ export async function runRecommendWorkflow({
|
|
|
596
624
|
}
|
|
597
625
|
|
|
598
626
|
const index = results.length;
|
|
599
|
-
|
|
627
|
+
let cardNodeId = nextCandidateResult.item.node_id;
|
|
600
628
|
const candidateKey = nextCandidateResult.item.key;
|
|
601
|
-
|
|
629
|
+
let cardCandidate = nextCandidateResult.item.candidate;
|
|
602
630
|
|
|
603
631
|
let screeningCandidate = cardCandidate;
|
|
604
632
|
let detailResult = null;
|
|
@@ -608,7 +636,17 @@ export async function runRecommendWorkflow({
|
|
|
608
636
|
runControl.setPhase("recommend:detail");
|
|
609
637
|
rootState = await ensureRecommendViewport(rootState, "detail");
|
|
610
638
|
networkRecorder.clear();
|
|
611
|
-
const openedDetail = await
|
|
639
|
+
const openedDetail = await openRecommendCardDetailWithFreshRetry(client, {
|
|
640
|
+
cardNodeId,
|
|
641
|
+
candidateKey,
|
|
642
|
+
cardCandidate,
|
|
643
|
+
rootState,
|
|
644
|
+
targetUrl,
|
|
645
|
+
maxAttempts: 2
|
|
646
|
+
});
|
|
647
|
+
cardNodeId = openedDetail.card_node_id || cardNodeId;
|
|
648
|
+
cardCandidate = openedDetail.card_candidate || cardCandidate;
|
|
649
|
+
screeningCandidate = cardCandidate;
|
|
612
650
|
const waitPlan = getCvNetworkWaitPlan(cvAcquisitionState);
|
|
613
651
|
const networkWait = await waitForCvNetworkEvents(
|
|
614
652
|
waitForRecommendDetailNetworkEvents,
|
|
@@ -686,7 +724,30 @@ export async function runRecommendWorkflow({
|
|
|
686
724
|
await runControl.waitIfPaused();
|
|
687
725
|
runControl.throwIfCanceled();
|
|
688
726
|
runControl.setPhase("recommend:screening");
|
|
689
|
-
|
|
727
|
+
let llmResult = null;
|
|
728
|
+
if (useLlmScreening) {
|
|
729
|
+
if (!llmConfig) {
|
|
730
|
+
llmResult = createMissingLlmConfigResult();
|
|
731
|
+
} else {
|
|
732
|
+
try {
|
|
733
|
+
llmResult = await callScreeningLlm({
|
|
734
|
+
candidate: screeningCandidate,
|
|
735
|
+
criteria,
|
|
736
|
+
config: llmConfig,
|
|
737
|
+
timeoutMs: llmTimeoutMs,
|
|
738
|
+
imageEvidence: detailResult?.image_evidence || null,
|
|
739
|
+
maxImages: llmImageLimit,
|
|
740
|
+
imageDetail: llmImageDetail
|
|
741
|
+
});
|
|
742
|
+
} catch (error) {
|
|
743
|
+
llmResult = createFailedLlmScreeningResult(error);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
if (detailResult) detailResult.llm_result = llmResult;
|
|
747
|
+
}
|
|
748
|
+
const screening = useLlmScreening
|
|
749
|
+
? llmResultToScreening(llmResult, screeningCandidate)
|
|
750
|
+
: screenCandidate(screeningCandidate, { criteria });
|
|
690
751
|
let actionDiscovery = null;
|
|
691
752
|
let postActionResult = null;
|
|
692
753
|
if (postActionEnabled && detailResult) {
|
|
@@ -721,6 +782,7 @@ export async function runRecommendWorkflow({
|
|
|
721
782
|
card_node_id: cardNodeId,
|
|
722
783
|
candidate: compactCandidate(screeningCandidate),
|
|
723
784
|
detail: compactDetail(detailResult),
|
|
785
|
+
llm_screening: detailResult ? null : compactScreeningLlmResult(llmResult),
|
|
724
786
|
screening: compactScreening(screening),
|
|
725
787
|
action_discovery: compactActionDiscovery(actionDiscovery),
|
|
726
788
|
post_action: postActionResult
|
|
@@ -740,6 +802,7 @@ export async function runRecommendWorkflow({
|
|
|
740
802
|
screened: results.length,
|
|
741
803
|
detail_opened: results.filter((item) => item.detail).length,
|
|
742
804
|
passed: results.filter((item) => item.screening.passed).length,
|
|
805
|
+
llm_screened: results.filter((item) => item.detail?.llm_screening || item.llm_screening).length,
|
|
743
806
|
greet_count: greetCount,
|
|
744
807
|
post_action_clicked: results.filter((item) => item.post_action?.action_clicked).length,
|
|
745
808
|
unique_seen: compactInfiniteListState(listState).seen_count,
|
|
@@ -754,6 +817,7 @@ export async function runRecommendWorkflow({
|
|
|
754
817
|
last_score: screening.score
|
|
755
818
|
});
|
|
756
819
|
runControl.checkpoint({
|
|
820
|
+
results,
|
|
757
821
|
last_candidate: {
|
|
758
822
|
id: screeningCandidate.id || null,
|
|
759
823
|
key: candidateKey,
|
|
@@ -763,6 +827,7 @@ export async function runRecommendWorkflow({
|
|
|
763
827
|
passed: screening.passed,
|
|
764
828
|
score: screening.score
|
|
765
829
|
},
|
|
830
|
+
llm_screening: compactScreeningLlmResult(llmResult),
|
|
766
831
|
post_action: postActionResult
|
|
767
832
|
}
|
|
768
833
|
});
|
|
@@ -796,6 +861,7 @@ export async function runRecommendWorkflow({
|
|
|
796
861
|
processed: results.length,
|
|
797
862
|
screened: results.length,
|
|
798
863
|
detail_opened: results.filter((item) => item.detail).length,
|
|
864
|
+
llm_screened: results.filter((item) => item.detail?.llm_screening || item.llm_screening).length,
|
|
799
865
|
passed: results.filter((item) => item.screening.passed).length,
|
|
800
866
|
greet_count: greetCount,
|
|
801
867
|
post_action_clicked: results.filter((item) => item.post_action?.action_clicked).length,
|
|
@@ -841,6 +907,11 @@ export function createRecommendRunService({
|
|
|
841
907
|
actionTimeoutMs = 8000,
|
|
842
908
|
actionIntervalMs = 500,
|
|
843
909
|
actionAfterClickDelayMs = 900,
|
|
910
|
+
screeningMode = "llm",
|
|
911
|
+
llmConfig = null,
|
|
912
|
+
llmTimeoutMs = 120000,
|
|
913
|
+
llmImageLimit = 8,
|
|
914
|
+
llmImageDetail = "high",
|
|
844
915
|
name = "recommend-domain-run"
|
|
845
916
|
} = {}) {
|
|
846
917
|
if (!client) throw new Error("startRecommendRun requires a guarded CDP client");
|
|
@@ -848,6 +919,7 @@ export function createRecommendRunService({
|
|
|
848
919
|
const normalizedPostAction = normalizeRecommendPostAction(postAction) || "none";
|
|
849
920
|
const requestedPageScope = normalizeRecommendPageScope(pageScope) || "recommend";
|
|
850
921
|
const normalizedFallbackPageScope = normalizeRecommendPageScope(fallbackPageScope) || "recommend";
|
|
922
|
+
const normalizedScreeningMode = normalizeScreeningMode(screeningMode);
|
|
851
923
|
const candidateLimit = Math.max(1, Number(maxCandidates) || 1);
|
|
852
924
|
const normalizedDetailLimit = detailLimit == null ? candidateLimit : Math.max(0, Number(detailLimit) || 0);
|
|
853
925
|
return manager.startRun({
|
|
@@ -878,7 +950,12 @@ export function createRecommendRunService({
|
|
|
878
950
|
post_action: normalizedPostAction,
|
|
879
951
|
max_greet_count: Number.isInteger(maxGreetCount) ? maxGreetCount : null,
|
|
880
952
|
execute_post_action: Boolean(executePostAction),
|
|
881
|
-
action_timeout_ms: actionTimeoutMs
|
|
953
|
+
action_timeout_ms: actionTimeoutMs,
|
|
954
|
+
screening_mode: normalizedScreeningMode,
|
|
955
|
+
llm_configured: Boolean(llmConfig),
|
|
956
|
+
llm_timeout_ms: llmTimeoutMs,
|
|
957
|
+
llm_image_limit: llmImageLimit,
|
|
958
|
+
llm_image_detail: llmImageDetail
|
|
882
959
|
},
|
|
883
960
|
progress: {
|
|
884
961
|
card_count: 0,
|
|
@@ -886,6 +963,7 @@ export function createRecommendRunService({
|
|
|
886
963
|
processed: 0,
|
|
887
964
|
screened: 0,
|
|
888
965
|
detail_opened: 0,
|
|
966
|
+
llm_screened: 0,
|
|
889
967
|
passed: 0,
|
|
890
968
|
greet_count: 0,
|
|
891
969
|
post_action_clicked: 0
|
|
@@ -921,7 +999,12 @@ export function createRecommendRunService({
|
|
|
921
999
|
executePostAction,
|
|
922
1000
|
actionTimeoutMs,
|
|
923
1001
|
actionIntervalMs,
|
|
924
|
-
actionAfterClickDelayMs
|
|
1002
|
+
actionAfterClickDelayMs,
|
|
1003
|
+
screeningMode: normalizedScreeningMode,
|
|
1004
|
+
llmConfig,
|
|
1005
|
+
llmTimeoutMs,
|
|
1006
|
+
llmImageLimit,
|
|
1007
|
+
llmImageDetail
|
|
925
1008
|
}, runControl)
|
|
926
1009
|
});
|
|
927
1010
|
}
|