@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
package/README.md
CHANGED
|
@@ -98,6 +98,8 @@ boss-recommend-mcp list-jobs --slow-live --port 9222
|
|
|
98
98
|
- 不会对每位候选人重复确认
|
|
99
99
|
- 推荐页详情处理完成后,会强制关闭详情页并确认已关闭
|
|
100
100
|
- 简历提取优先使用 Network 响应;没有可解析 Network CV 时,回退到完整滚动截图序列再交给多模态模型判断
|
|
101
|
+
- recommend / search / chat 正式运行默认全部使用 `screening-config.json` 配置的 LLM 筛选;deterministic/local scorer 只保留给明确测试场景,必须显式传 `debug_test_mode=true` 且 `screening_mode=deterministic` 或 `use_llm=false`。
|
|
102
|
+
- `detail_limit=0`、`no_filter`、`filter_enabled=false`、后置动作 dry-run、chat 求简历 dry-run 等调试路径不会在正式 live run 默认启用;需要测试时必须显式传 `debug_test_mode=true`。
|
|
101
103
|
- 提供显式运维自愈工具:只在手动调用 `run_recommend_self_heal` 时运行,不会接入正常 run / doctor / preflight 自动链路
|
|
102
104
|
- 运行前会自动做依赖体检(Node.js、Python、Pillow、`chrome-remote-interface`、`ws`),缺失时会在 `doctor` 与流水线失败诊断中明确提示
|
|
103
105
|
- 若 preflight 失败,返回 `diagnostics.recovery`(含有序修复步骤与 `agent_prompt`),可直接交给 AI agent 自动按顺序安装依赖
|
package/package.json
CHANGED
package/src/chat-mcp.js
CHANGED
|
@@ -308,8 +308,15 @@ function ensureChatRunArtifacts(snapshot) {
|
|
|
308
308
|
if (meta) meta.checkpointPath = artifacts.checkpoint_path;
|
|
309
309
|
|
|
310
310
|
const summary = snapshot?.summary && typeof snapshot.summary === "object" ? snapshot.summary : null;
|
|
311
|
-
|
|
312
|
-
|
|
311
|
+
const checkpointResults = Array.isArray(checkpoint.results) ? checkpoint.results : [];
|
|
312
|
+
const artifactSummary = summary || (checkpointResults.length ? {
|
|
313
|
+
domain: "chat",
|
|
314
|
+
partial: true,
|
|
315
|
+
partial_reason: snapshot?.status || snapshot?.state || "non_terminal",
|
|
316
|
+
results: checkpointResults
|
|
317
|
+
} : null);
|
|
318
|
+
if (artifactSummary) {
|
|
319
|
+
const rows = Array.isArray(artifactSummary.results) ? artifactSummary.results : [];
|
|
313
320
|
writeChatLegacyCsvAtomic(artifacts.output_csv, rows, snapshot, meta);
|
|
314
321
|
writeJsonAtomic(artifacts.report_json, {
|
|
315
322
|
run_id: snapshot.runId || snapshot.run_id,
|
|
@@ -318,7 +325,7 @@ function ensureChatRunArtifacts(snapshot) {
|
|
|
318
325
|
progress: snapshot.progress || {},
|
|
319
326
|
context: snapshot.context || {},
|
|
320
327
|
checkpoint,
|
|
321
|
-
summary,
|
|
328
|
+
summary: artifactSummary,
|
|
322
329
|
generated_at: new Date().toISOString()
|
|
323
330
|
});
|
|
324
331
|
if (meta) {
|
|
@@ -335,6 +342,12 @@ function buildLegacyChatResult(snapshot) {
|
|
|
335
342
|
const artifacts = ensureChatRunArtifacts(snapshot);
|
|
336
343
|
const meta = getChatRunMeta(snapshot.runId);
|
|
337
344
|
const summary = snapshot.summary && typeof snapshot.summary === "object" ? snapshot.summary : null;
|
|
345
|
+
const checkpoint = snapshot.checkpoint && typeof snapshot.checkpoint === "object" ? snapshot.checkpoint : {};
|
|
346
|
+
const resultRows = Array.isArray(summary?.results)
|
|
347
|
+
? summary.results
|
|
348
|
+
: Array.isArray(checkpoint.results)
|
|
349
|
+
? checkpoint.results
|
|
350
|
+
: [];
|
|
338
351
|
const progress = normalizeLegacyProgress(snapshot.progress, summary);
|
|
339
352
|
return {
|
|
340
353
|
run_id: snapshot.runId,
|
|
@@ -358,7 +371,7 @@ function buildLegacyChatResult(snapshot) {
|
|
|
358
371
|
completed_at: snapshot.completedAt || null,
|
|
359
372
|
duration_sec: secondsBetween(snapshot.startedAt, snapshot.completedAt),
|
|
360
373
|
error: snapshot.error || null,
|
|
361
|
-
results:
|
|
374
|
+
results: resultRows
|
|
362
375
|
};
|
|
363
376
|
}
|
|
364
377
|
|
|
@@ -788,16 +801,31 @@ function shouldRequestChatResume(args = {}) {
|
|
|
788
801
|
);
|
|
789
802
|
}
|
|
790
803
|
|
|
791
|
-
function
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
);
|
|
804
|
+
function isDebugTestMode(args = {}) {
|
|
805
|
+
return args.debug_test_mode === true || args.allow_debug_test_mode === true;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
function normalizeScreeningModeArg(args = {}) {
|
|
809
|
+
const raw = normalizeText(args.screening_mode || args.screeningMode || "");
|
|
810
|
+
if (args.use_llm === false) return "deterministic";
|
|
811
|
+
return ["deterministic", "local", "local_scorer"].includes(raw.toLowerCase())
|
|
812
|
+
? "deterministic"
|
|
813
|
+
: "llm";
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
function collectChatDebugTestOptions(args = {}) {
|
|
817
|
+
const reasons = [];
|
|
818
|
+
if (normalizeScreeningModeArg(args) === "deterministic") reasons.push("deterministic_screening");
|
|
819
|
+
if (parseNonNegativeInteger(args.detail_limit, null) === 0) reasons.push("detail_limit=0");
|
|
820
|
+
if (args.dry_run === true || args.dry_run_request_cv === true) reasons.push("dry_run_request_cv");
|
|
821
|
+
return reasons;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
function shouldUseChatLlm(args = {}) {
|
|
825
|
+
return normalizeScreeningModeArg(args) !== "deterministic";
|
|
798
826
|
}
|
|
799
827
|
|
|
800
|
-
function getRunOptions(args, normalized, session, { workspaceRoot = "" } = {}) {
|
|
828
|
+
function getRunOptions(args, normalized, session, { workspaceRoot = "", configResolution = null } = {}) {
|
|
801
829
|
const slowLive = args.slow_live === true;
|
|
802
830
|
const isAllTarget = normalized.publicTargetCount === "all";
|
|
803
831
|
const processedLimit = parsePositiveInteger(
|
|
@@ -805,8 +833,8 @@ function getRunOptions(args, normalized, session, { workspaceRoot = "" } = {}) {
|
|
|
805
833
|
isAllTarget ? CHAT_ALL_MAX_CANDIDATES : CHAT_ALL_MAX_CANDIDATES
|
|
806
834
|
);
|
|
807
835
|
const shouldRequestResume = shouldRequestChatResume(args);
|
|
808
|
-
const useLlm = shouldUseChatLlm(args
|
|
809
|
-
const
|
|
836
|
+
const useLlm = shouldUseChatLlm(args);
|
|
837
|
+
const resolvedConfig = configResolution || (useLlm ? resolveBossScreeningConfig(workspaceRoot) : { ok: false });
|
|
810
838
|
return {
|
|
811
839
|
client: session.client,
|
|
812
840
|
targetUrl: CHAT_TARGET_URL,
|
|
@@ -832,12 +860,13 @@ function getRunOptions(args, normalized, session, { workspaceRoot = "" } = {}) {
|
|
|
832
860
|
resumeDomTimeoutMs: slowLive ? 120000 : 60000,
|
|
833
861
|
maxImagePages: parsePositiveInteger(args.max_image_pages, 8),
|
|
834
862
|
imageWheelDeltaY: parsePositiveInteger(args.image_wheel_delta_y, 650),
|
|
835
|
-
llmConfig:
|
|
836
|
-
...
|
|
863
|
+
llmConfig: resolvedConfig.ok ? {
|
|
864
|
+
...resolvedConfig.config
|
|
837
865
|
} : null,
|
|
838
866
|
llmTimeoutMs: parsePositiveInteger(args.llm_timeout_ms, slowLive ? 180000 : 120000),
|
|
839
867
|
llmImageLimit: parsePositiveInteger(args.llm_image_limit, 8),
|
|
840
868
|
llmImageDetail: normalizeText(args.llm_image_detail) || "high",
|
|
869
|
+
screeningMode: normalizeScreeningModeArg(args),
|
|
841
870
|
listMaxScrolls: parsePositiveInteger(args.list_max_scrolls, 200),
|
|
842
871
|
listStableSignatureLimit: parsePositiveInteger(args.list_stable_signature_limit, 2),
|
|
843
872
|
listWheelDeltaY: parsePositiveInteger(args.list_wheel_delta_y, 850),
|
|
@@ -905,7 +934,19 @@ async function startBossChatRunInternal(args = {}, { workspaceRoot = "" } = {})
|
|
|
905
934
|
}
|
|
906
935
|
|
|
907
936
|
const shouldRequestResume = shouldRequestChatResume(args);
|
|
908
|
-
const useLlm = shouldUseChatLlm(args
|
|
937
|
+
const useLlm = shouldUseChatLlm(args);
|
|
938
|
+
const debugTestOptions = collectChatDebugTestOptions(args);
|
|
939
|
+
if (debugTestOptions.length && !isDebugTestMode(args)) {
|
|
940
|
+
return {
|
|
941
|
+
status: "FAILED",
|
|
942
|
+
error: {
|
|
943
|
+
code: "DEBUG_TEST_MODE_REQUIRED",
|
|
944
|
+
message: `这些参数属于调试/测试路径,正式 live run 不会默认启用:${debugTestOptions.join(", ")}。如确需测试,请显式传 debug_test_mode=true。`,
|
|
945
|
+
retryable: false
|
|
946
|
+
},
|
|
947
|
+
debug_test_options: debugTestOptions
|
|
948
|
+
};
|
|
949
|
+
}
|
|
909
950
|
const configResolution = useLlm ? resolveBossScreeningConfig(workspaceRoot) : null;
|
|
910
951
|
if (useLlm && !configResolution?.ok) {
|
|
911
952
|
return {
|
|
@@ -948,7 +989,7 @@ async function startBossChatRunInternal(args = {}, { workspaceRoot = "" } = {})
|
|
|
948
989
|
|
|
949
990
|
let started;
|
|
950
991
|
try {
|
|
951
|
-
started = chatRunService.startChatRun(getRunOptions(args, normalized, session, { workspaceRoot }));
|
|
992
|
+
started = chatRunService.startChatRun(getRunOptions(args, normalized, session, { workspaceRoot, configResolution }));
|
|
952
993
|
} catch (error) {
|
|
953
994
|
await session.close?.();
|
|
954
995
|
return {
|
|
@@ -284,8 +284,8 @@ export function legacyScreenResultRow(row = {}) {
|
|
|
284
284
|
totalEvidence,
|
|
285
285
|
totalEvidence,
|
|
286
286
|
"",
|
|
287
|
-
row.error_code || error.code || error.name || "",
|
|
288
|
-
row.error_message || error.message || "",
|
|
287
|
+
row.error_code || error.code || error.name || (llm.error ? "LLM_SCREENING_ERROR" : ""),
|
|
288
|
+
row.error_message || error.message || llm.error || "",
|
|
289
289
|
candidate.id || row.candidate_id || "",
|
|
290
290
|
timingValue(row, "total_ms"),
|
|
291
291
|
timingValue(row, "card_read_ms"),
|
|
@@ -1003,6 +1003,54 @@ export function screenCandidate(candidateInput, criteria = {}) {
|
|
|
1003
1003
|
};
|
|
1004
1004
|
}
|
|
1005
1005
|
|
|
1006
|
+
export function compactScreeningLlmResult(llmResult) {
|
|
1007
|
+
if (!llmResult) return null;
|
|
1008
|
+
return {
|
|
1009
|
+
ok: Boolean(llmResult.ok),
|
|
1010
|
+
provider: llmResult.provider || null,
|
|
1011
|
+
passed: llmResult.passed,
|
|
1012
|
+
cot: llmResult.cot || llmResult.decision_cot || "",
|
|
1013
|
+
reasoning_content: llmResult.reasoning_content || "",
|
|
1014
|
+
raw_model_output: llmResult.raw_model_output || "",
|
|
1015
|
+
evidence_count: Array.isArray(llmResult.evidence) ? llmResult.evidence.length : 0,
|
|
1016
|
+
usage: llmResult.usage || null,
|
|
1017
|
+
finish_reason: llmResult.finish_reason || null,
|
|
1018
|
+
image_input_count: llmResult.image_input_count || 0,
|
|
1019
|
+
error: llmResult.error || null,
|
|
1020
|
+
screened_at: llmResult.screened_at || null
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
export function llmResultToScreening(llmResult, candidate) {
|
|
1025
|
+
return {
|
|
1026
|
+
status: llmResult?.passed ? "pass" : "fail",
|
|
1027
|
+
passed: Boolean(llmResult?.passed),
|
|
1028
|
+
score: llmResult?.passed ? 100 : 0,
|
|
1029
|
+
reasons: llmResult?.error ? ["llm_invalid_response"] : [],
|
|
1030
|
+
candidate
|
|
1031
|
+
};
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
export function isRecoverableLlmScreeningError(error) {
|
|
1035
|
+
return /(?:LLM response missing boolean passed decision|LLM response was not valid JSON)/i
|
|
1036
|
+
.test(String(error?.message || error || ""));
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
export function createFailedLlmScreeningResult(error) {
|
|
1040
|
+
return {
|
|
1041
|
+
ok: false,
|
|
1042
|
+
passed: false,
|
|
1043
|
+
reason: "",
|
|
1044
|
+
evidence: [],
|
|
1045
|
+
cot: "",
|
|
1046
|
+
decision_cot: "",
|
|
1047
|
+
reasoning_content: "",
|
|
1048
|
+
raw_model_output: "",
|
|
1049
|
+
error: error?.message || String(error || "unknown"),
|
|
1050
|
+
screened_at: nowIso()
|
|
1051
|
+
};
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1006
1054
|
export function buildScreeningLlmMessages({
|
|
1007
1055
|
candidate,
|
|
1008
1056
|
criteria,
|
|
@@ -167,6 +167,17 @@ function createFailedLlmResult(error) {
|
|
|
167
167
|
};
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
+
function normalizeScreeningMode(value) {
|
|
171
|
+
const normalized = String(value || "llm").trim().toLowerCase();
|
|
172
|
+
return ["deterministic", "local", "local_scorer"].includes(normalized)
|
|
173
|
+
? "deterministic"
|
|
174
|
+
: "llm";
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function createMissingLlmConfigResult() {
|
|
178
|
+
return createFailedLlmResult(new Error("LLM screening config is required for production chat runs"));
|
|
179
|
+
}
|
|
180
|
+
|
|
170
181
|
function createSkippedDetailResult(cardCandidate, reason, error = null) {
|
|
171
182
|
return {
|
|
172
183
|
candidate: cardCandidate,
|
|
@@ -334,7 +345,7 @@ export async function runChatWorkflow({
|
|
|
334
345
|
maxCandidates = 5,
|
|
335
346
|
targetPassCount = null,
|
|
336
347
|
processUntilListEnd = false,
|
|
337
|
-
detailLimit =
|
|
348
|
+
detailLimit = null,
|
|
338
349
|
detailSource = "cascade",
|
|
339
350
|
closeResume = true,
|
|
340
351
|
requestResumeForPassed = false,
|
|
@@ -353,6 +364,7 @@ export async function runChatWorkflow({
|
|
|
353
364
|
llmTimeoutMs = 120000,
|
|
354
365
|
llmImageLimit = 8,
|
|
355
366
|
llmImageDetail = "high",
|
|
367
|
+
screeningMode = "llm",
|
|
356
368
|
listMaxScrolls = 20,
|
|
357
369
|
listStableSignatureLimit = 2,
|
|
358
370
|
listWheelDeltaY = 850,
|
|
@@ -361,12 +373,14 @@ export async function runChatWorkflow({
|
|
|
361
373
|
} = {}, runControl) {
|
|
362
374
|
if (!client) throw new Error("runChatWorkflow requires a guarded CDP client");
|
|
363
375
|
const normalizedDetailSource = normalizeDetailSource(detailSource);
|
|
376
|
+
const normalizedScreeningMode = normalizeScreeningMode(screeningMode);
|
|
377
|
+
const useLlmScreening = normalizedScreeningMode !== "deterministic";
|
|
364
378
|
const processedLimit = Math.max(1, Number(maxCandidates) || 1);
|
|
365
379
|
const passTarget = Number.isFinite(Number(targetPassCount)) && Number(targetPassCount) > 0
|
|
366
380
|
? Number(targetPassCount)
|
|
367
381
|
: null;
|
|
368
382
|
const normalizedStartFrom = normalizeText(startFrom).toLowerCase() === "unread" ? "unread" : "all";
|
|
369
|
-
const detailCountLimit = Math.max(0, Number(detailLimit) || 0);
|
|
383
|
+
const detailCountLimit = detailLimit == null ? processedLimit : Math.max(0, Number(detailLimit) || 0);
|
|
370
384
|
const networkRecorder = detailCountLimit > 0
|
|
371
385
|
? createChatProfileNetworkRecorder(client)
|
|
372
386
|
: null;
|
|
@@ -556,6 +570,7 @@ export async function runChatWorkflow({
|
|
|
556
570
|
requested: 0,
|
|
557
571
|
request_satisfied: 0,
|
|
558
572
|
request_skipped: 0,
|
|
573
|
+
screening_mode: normalizedScreeningMode,
|
|
559
574
|
unique_seen: compactInfiniteListState(listState).seen_count,
|
|
560
575
|
scroll_count: 0,
|
|
561
576
|
viewport_checks: viewportGuard.getStats().checks,
|
|
@@ -799,21 +814,23 @@ export async function runChatWorkflow({
|
|
|
799
814
|
imageEvidence
|
|
800
815
|
});
|
|
801
816
|
if (callLlmOnImage) {
|
|
802
|
-
if (!llmConfig) throw new Error("callLlmOnImage requires llmConfig");
|
|
803
817
|
detailStep = "llm_image_screening";
|
|
804
|
-
|
|
805
|
-
llmResult =
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
818
|
+
if (!llmConfig) {
|
|
819
|
+
llmResult = createMissingLlmConfigResult();
|
|
820
|
+
} else {
|
|
821
|
+
try {
|
|
822
|
+
llmResult = await callScreeningLlm({
|
|
823
|
+
candidate: detailResult.candidate,
|
|
824
|
+
criteria,
|
|
825
|
+
config: llmConfig,
|
|
826
|
+
timeoutMs: llmTimeoutMs,
|
|
827
|
+
imageEvidence,
|
|
828
|
+
maxImages: llmImageLimit,
|
|
829
|
+
imageDetail: llmImageDetail
|
|
830
|
+
});
|
|
831
|
+
} catch (error) {
|
|
832
|
+
llmResult = createFailedLlmResult(error);
|
|
833
|
+
}
|
|
817
834
|
}
|
|
818
835
|
}
|
|
819
836
|
} else {
|
|
@@ -838,21 +855,24 @@ export async function runChatWorkflow({
|
|
|
838
855
|
});
|
|
839
856
|
}
|
|
840
857
|
|
|
841
|
-
if (
|
|
858
|
+
if (useLlmScreening && !llmResult) {
|
|
842
859
|
detailStep = "llm_screening";
|
|
843
|
-
|
|
844
|
-
llmResult =
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
860
|
+
if (!llmConfig) {
|
|
861
|
+
llmResult = createMissingLlmConfigResult();
|
|
862
|
+
} else {
|
|
863
|
+
try {
|
|
864
|
+
llmResult = await callScreeningLlm({
|
|
865
|
+
candidate: detailResult.candidate,
|
|
866
|
+
criteria,
|
|
867
|
+
config: llmConfig,
|
|
868
|
+
timeoutMs: llmTimeoutMs,
|
|
869
|
+
imageEvidence,
|
|
870
|
+
maxImages: llmImageLimit,
|
|
871
|
+
imageDetail: llmImageDetail
|
|
872
|
+
});
|
|
873
|
+
} catch (error) {
|
|
874
|
+
llmResult = createFailedLlmResult(error);
|
|
875
|
+
}
|
|
856
876
|
}
|
|
857
877
|
}
|
|
858
878
|
|
|
@@ -901,6 +921,26 @@ export async function runChatWorkflow({
|
|
|
901
921
|
await runControl.waitIfPaused();
|
|
902
922
|
runControl.throwIfCanceled();
|
|
903
923
|
runControl.setPhase("chat:screening");
|
|
924
|
+
let cardOnlyLlmResult = null;
|
|
925
|
+
if (useLlmScreening && !detailUnavailableReason && !detailResult?.llm_result) {
|
|
926
|
+
if (!llmConfig) {
|
|
927
|
+
cardOnlyLlmResult = createMissingLlmConfigResult();
|
|
928
|
+
} else {
|
|
929
|
+
try {
|
|
930
|
+
cardOnlyLlmResult = await callScreeningLlm({
|
|
931
|
+
candidate: screeningCandidate,
|
|
932
|
+
criteria,
|
|
933
|
+
config: llmConfig,
|
|
934
|
+
timeoutMs: llmTimeoutMs,
|
|
935
|
+
maxImages: llmImageLimit,
|
|
936
|
+
imageDetail: llmImageDetail
|
|
937
|
+
});
|
|
938
|
+
} catch (error) {
|
|
939
|
+
cardOnlyLlmResult = createFailedLlmResult(error);
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
const effectiveLlmResult = detailResult?.llm_result || cardOnlyLlmResult;
|
|
904
944
|
const screening = detailUnavailableReason
|
|
905
945
|
? {
|
|
906
946
|
status: "skip",
|
|
@@ -909,8 +949,8 @@ export async function runChatWorkflow({
|
|
|
909
949
|
reasons: [detailUnavailableReason],
|
|
910
950
|
candidate: screeningCandidate
|
|
911
951
|
}
|
|
912
|
-
:
|
|
913
|
-
? llmToScreening(
|
|
952
|
+
: useLlmScreening
|
|
953
|
+
? llmToScreening(effectiveLlmResult, screeningCandidate)
|
|
914
954
|
: screenCandidate(screeningCandidate, { criteria });
|
|
915
955
|
let postAction = null;
|
|
916
956
|
if (requestResumeForPassed && screening.passed) {
|
|
@@ -931,6 +971,7 @@ export async function runChatWorkflow({
|
|
|
931
971
|
card_node_id: effectiveCardNodeId,
|
|
932
972
|
candidate: compactCandidate(screeningCandidate),
|
|
933
973
|
detail: compactDetail(detailResult),
|
|
974
|
+
llm_screening: detailResult ? null : compactLlmResult(cardOnlyLlmResult),
|
|
934
975
|
screening: compactScreening(screening),
|
|
935
976
|
post_action: postAction,
|
|
936
977
|
pre_action_state: preActionState
|
|
@@ -951,7 +992,7 @@ export async function runChatWorkflow({
|
|
|
951
992
|
processed: results.length,
|
|
952
993
|
screened: results.length,
|
|
953
994
|
detail_opened: results.filter(resultOpenedDetail).length,
|
|
954
|
-
llm_screened: results.filter((item) => item.detail?.llm_screening).length,
|
|
995
|
+
llm_screened: results.filter((item) => item.detail?.llm_screening || item.llm_screening).length,
|
|
955
996
|
passed: results.filter((item) => item.screening.passed).length,
|
|
956
997
|
requested: requestedCount,
|
|
957
998
|
request_satisfied: requestSatisfiedCount,
|
|
@@ -966,6 +1007,7 @@ export async function runChatWorkflow({
|
|
|
966
1007
|
last_score: screening.score
|
|
967
1008
|
});
|
|
968
1009
|
runControl.checkpoint({
|
|
1010
|
+
results,
|
|
969
1011
|
last_candidate: {
|
|
970
1012
|
id: screeningCandidate.id || null,
|
|
971
1013
|
key: candidateKey,
|
|
@@ -974,7 +1016,8 @@ export async function runChatWorkflow({
|
|
|
974
1016
|
status: screening.status,
|
|
975
1017
|
passed: screening.passed,
|
|
976
1018
|
score: screening.score
|
|
977
|
-
}
|
|
1019
|
+
},
|
|
1020
|
+
llm_screening: compactLlmResult(effectiveLlmResult)
|
|
978
1021
|
}
|
|
979
1022
|
});
|
|
980
1023
|
|
|
@@ -1002,7 +1045,7 @@ export async function runChatWorkflow({
|
|
|
1002
1045
|
processed: results.length,
|
|
1003
1046
|
screened: results.length,
|
|
1004
1047
|
detail_opened: results.filter(resultOpenedDetail).length,
|
|
1005
|
-
llm_screened: results.filter((item) => item.detail?.llm_screening).length,
|
|
1048
|
+
llm_screened: results.filter((item) => item.detail?.llm_screening || item.llm_screening).length,
|
|
1006
1049
|
passed: results.filter((item) => item.screening.passed).length,
|
|
1007
1050
|
requested: requestedCount,
|
|
1008
1051
|
request_satisfied: requestSatisfiedCount,
|
|
@@ -1027,7 +1070,7 @@ export function createChatRunService({
|
|
|
1027
1070
|
maxCandidates = 5,
|
|
1028
1071
|
targetPassCount = null,
|
|
1029
1072
|
processUntilListEnd = false,
|
|
1030
|
-
detailLimit =
|
|
1073
|
+
detailLimit = null,
|
|
1031
1074
|
detailSource = "cascade",
|
|
1032
1075
|
closeResume = true,
|
|
1033
1076
|
requestResumeForPassed = false,
|
|
@@ -1046,6 +1089,7 @@ export function createChatRunService({
|
|
|
1046
1089
|
llmTimeoutMs = 120000,
|
|
1047
1090
|
llmImageLimit = 8,
|
|
1048
1091
|
llmImageDetail = "high",
|
|
1092
|
+
screeningMode = "llm",
|
|
1049
1093
|
listMaxScrolls = 20,
|
|
1050
1094
|
listStableSignatureLimit = 2,
|
|
1051
1095
|
listWheelDeltaY = 850,
|
|
@@ -1055,6 +1099,9 @@ export function createChatRunService({
|
|
|
1055
1099
|
} = {}) {
|
|
1056
1100
|
if (!client) throw new Error("startChatRun requires a guarded CDP client");
|
|
1057
1101
|
const normalizedDetailSource = normalizeDetailSource(detailSource);
|
|
1102
|
+
const normalizedScreeningMode = normalizeScreeningMode(screeningMode);
|
|
1103
|
+
const processedLimit = Math.max(1, Number(maxCandidates) || 1);
|
|
1104
|
+
const normalizedDetailLimit = detailLimit == null ? processedLimit : Math.max(0, Number(detailLimit) || 0);
|
|
1058
1105
|
return manager.startRun({
|
|
1059
1106
|
name,
|
|
1060
1107
|
context: {
|
|
@@ -1066,11 +1113,16 @@ export function createChatRunService({
|
|
|
1066
1113
|
max_candidates: maxCandidates,
|
|
1067
1114
|
target_pass_count: targetPassCount,
|
|
1068
1115
|
process_until_list_end: Boolean(processUntilListEnd),
|
|
1069
|
-
detail_limit:
|
|
1116
|
+
detail_limit: normalizedDetailLimit,
|
|
1070
1117
|
detail_source: normalizedDetailSource,
|
|
1071
1118
|
close_resume: closeResume,
|
|
1072
1119
|
cv_acquisition_mode: cvAcquisitionMode,
|
|
1073
1120
|
call_llm_on_image: Boolean(callLlmOnImage),
|
|
1121
|
+
screening_mode: normalizedScreeningMode,
|
|
1122
|
+
llm_configured: Boolean(llmConfig),
|
|
1123
|
+
llm_timeout_ms: llmTimeoutMs,
|
|
1124
|
+
llm_image_limit: llmImageLimit,
|
|
1125
|
+
llm_image_detail: llmImageDetail,
|
|
1074
1126
|
max_image_pages: maxImagePages,
|
|
1075
1127
|
image_wheel_delta_y: imageWheelDeltaY,
|
|
1076
1128
|
list_max_scrolls: listMaxScrolls,
|
|
@@ -1082,9 +1134,9 @@ export function createChatRunService({
|
|
|
1082
1134
|
},
|
|
1083
1135
|
progress: {
|
|
1084
1136
|
card_count: 0,
|
|
1085
|
-
target_count: targetPassCount || (processUntilListEnd ? "all" :
|
|
1137
|
+
target_count: targetPassCount || (processUntilListEnd ? "all" : processedLimit),
|
|
1086
1138
|
target_pass_count: targetPassCount,
|
|
1087
|
-
processed_limit:
|
|
1139
|
+
processed_limit: processedLimit,
|
|
1088
1140
|
processed: 0,
|
|
1089
1141
|
screened: 0,
|
|
1090
1142
|
detail_opened: 0,
|
|
@@ -1104,7 +1156,7 @@ export function createChatRunService({
|
|
|
1104
1156
|
maxCandidates,
|
|
1105
1157
|
targetPassCount,
|
|
1106
1158
|
processUntilListEnd,
|
|
1107
|
-
detailLimit,
|
|
1159
|
+
detailLimit: normalizedDetailLimit,
|
|
1108
1160
|
detailSource: normalizedDetailSource,
|
|
1109
1161
|
closeResume,
|
|
1110
1162
|
requestResumeForPassed,
|
|
@@ -1123,6 +1175,7 @@ export function createChatRunService({
|
|
|
1123
1175
|
llmTimeoutMs,
|
|
1124
1176
|
llmImageLimit,
|
|
1125
1177
|
llmImageDetail,
|
|
1178
|
+
screeningMode: normalizedScreeningMode,
|
|
1126
1179
|
listMaxScrolls,
|
|
1127
1180
|
listStableSignatureLimit,
|
|
1128
1181
|
listWheelDeltaY,
|