@reconcrap/boss-recommend-mcp 2.0.7 → 2.0.9
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/chat-mcp.js +12 -3
- package/src/core/boss-cards/index.js +199 -0
- package/src/core/capture/index.js +126 -24
- package/src/core/cv-acquisition/index.js +6 -0
- package/src/core/reporting/legacy-csv.js +10 -1
- package/src/core/run/timing.js +33 -0
- package/src/core/screening/index.js +310 -27
- package/src/domains/chat/cards.js +9 -1
- package/src/domains/chat/detail.js +29 -18
- package/src/domains/chat/run-service.js +72 -28
- package/src/domains/recommend/cards.js +16 -1
- package/src/domains/recommend/detail.js +40 -19
- package/src/domains/recommend/run-service.js +60 -15
- package/src/domains/recruit/cards.js +9 -1
- package/src/domains/recruit/detail.js +41 -19
- package/src/domains/recruit/run-service.js +58 -15
- package/src/index.js +2 -2
- package/src/recommend-mcp.js +12 -3
- package/src/recruit-mcp.js +13 -4
|
@@ -206,11 +206,52 @@ function parseDateLike(value) {
|
|
|
206
206
|
return normalized;
|
|
207
207
|
}
|
|
208
208
|
|
|
209
|
+
function isLikelySalaryLine(value = "") {
|
|
210
|
+
const normalized = normalizeText(value);
|
|
211
|
+
return Boolean(
|
|
212
|
+
/^(?:面议|薪资面议)$/i.test(normalized)
|
|
213
|
+
|| /^\d+(?:\.\d+)?(?:\s*-\s*\d+(?:\.\d+)?)?\s*[kK](?:\s*[·xX*]\s*\d+\s*薪?)?$/.test(normalized)
|
|
214
|
+
|| /^\d+\s*-\s*\d+\s*元\s*\/\s*天$/.test(normalized)
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function isLikelyStatusLine(value = "") {
|
|
219
|
+
const normalized = normalizeText(value);
|
|
220
|
+
return Boolean(
|
|
221
|
+
!normalized
|
|
222
|
+
|| /^沟通|^收藏|^查看|^不合适/.test(normalized)
|
|
223
|
+
|| /^(?:在线|刚刚活跃|今日活跃|本周活跃|本月活跃|继续沟通|打招呼)$/.test(normalized)
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function stripLeadingSalaryToken(value = "") {
|
|
228
|
+
return normalizeText(value)
|
|
229
|
+
.replace(/^(?:面议|薪资面议)\s+/i, "")
|
|
230
|
+
.replace(/^\d+(?:\.\d+)?(?:\s*-\s*\d+(?:\.\d+)?)?\s*[kK](?:\s*[·xX*]\s*\d+\s*薪?)?\s+/, "")
|
|
231
|
+
.replace(/^\d+\s*-\s*\d+\s*元\s*\/\s*天\s+/, "")
|
|
232
|
+
.trim();
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function stripTrailingStatusToken(value = "") {
|
|
236
|
+
return normalizeText(value)
|
|
237
|
+
.replace(/\s*(?:在线|刚刚活跃|今日活跃|本周活跃|本月活跃|继续沟通|打招呼)$/u, "")
|
|
238
|
+
.trim();
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function cleanInferredNameLine(value = "") {
|
|
242
|
+
const withoutSalary = stripLeadingSalaryToken(value);
|
|
243
|
+
const withoutStatus = stripTrailingStatusToken(withoutSalary);
|
|
244
|
+
return withoutStatus && !isLikelyStatusLine(withoutStatus) && !isLikelySalaryLine(withoutStatus)
|
|
245
|
+
? withoutStatus
|
|
246
|
+
: "";
|
|
247
|
+
}
|
|
248
|
+
|
|
209
249
|
function firstUsefulLine(lines) {
|
|
210
|
-
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
}
|
|
250
|
+
for (const line of lines) {
|
|
251
|
+
const cleaned = cleanInferredNameLine(line);
|
|
252
|
+
if (cleaned) return cleaned;
|
|
253
|
+
}
|
|
254
|
+
return null;
|
|
214
255
|
}
|
|
215
256
|
|
|
216
257
|
function parseNetworkBodyText(networkBody = {}) {
|
|
@@ -248,6 +289,72 @@ function tryExtractJsonObject(text) {
|
|
|
248
289
|
return null;
|
|
249
290
|
}
|
|
250
291
|
|
|
292
|
+
function extractBalancedJsonAt(text = "", startIndex = 0) {
|
|
293
|
+
const source = String(text || "");
|
|
294
|
+
const start = source.indexOf("{", Math.max(0, Number(startIndex) || 0));
|
|
295
|
+
if (start < 0) return "";
|
|
296
|
+
let depth = 0;
|
|
297
|
+
let inString = false;
|
|
298
|
+
let quote = "";
|
|
299
|
+
let escaped = false;
|
|
300
|
+
for (let index = start; index < source.length; index += 1) {
|
|
301
|
+
const char = source[index];
|
|
302
|
+
if (inString) {
|
|
303
|
+
if (escaped) {
|
|
304
|
+
escaped = false;
|
|
305
|
+
} else if (char === "\\") {
|
|
306
|
+
escaped = true;
|
|
307
|
+
} else if (char === quote) {
|
|
308
|
+
inString = false;
|
|
309
|
+
quote = "";
|
|
310
|
+
}
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
if (char === "\"" || char === "'") {
|
|
314
|
+
inString = true;
|
|
315
|
+
quote = char;
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
if (char === "{") depth += 1;
|
|
319
|
+
if (char === "}") {
|
|
320
|
+
depth -= 1;
|
|
321
|
+
if (depth === 0) return source.slice(start, index + 1);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return "";
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function tryParseEmbeddedJsonObjects(text = "") {
|
|
328
|
+
const source = decodeHtmlEntities(String(text || ""));
|
|
329
|
+
const objects = [];
|
|
330
|
+
const anchors = [
|
|
331
|
+
"__INITIAL_STATE__",
|
|
332
|
+
"__NEXT_DATA__",
|
|
333
|
+
"geekDetailInfo",
|
|
334
|
+
"geekDetail",
|
|
335
|
+
"geekBaseInfo",
|
|
336
|
+
"geekEduExpList",
|
|
337
|
+
"geekWorkExpList",
|
|
338
|
+
"resume"
|
|
339
|
+
];
|
|
340
|
+
for (const anchor of anchors) {
|
|
341
|
+
let searchIndex = 0;
|
|
342
|
+
while (searchIndex >= 0 && searchIndex < source.length) {
|
|
343
|
+
const anchorIndex = source.indexOf(anchor, searchIndex);
|
|
344
|
+
if (anchorIndex < 0) break;
|
|
345
|
+
const windowStart = Math.max(0, anchorIndex - 4000);
|
|
346
|
+
const braceIndex = source.lastIndexOf("{", anchorIndex);
|
|
347
|
+
if (braceIndex >= windowStart) {
|
|
348
|
+
const jsonText = extractBalancedJsonAt(source, braceIndex);
|
|
349
|
+
const parsed = tryParseJson(jsonText);
|
|
350
|
+
if (parsed && typeof parsed === "object") objects.push(parsed);
|
|
351
|
+
}
|
|
352
|
+
searchIndex = anchorIndex + anchor.length;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
return objects;
|
|
356
|
+
}
|
|
357
|
+
|
|
251
358
|
function flattenChatMessageContent(content) {
|
|
252
359
|
if (typeof content === "string") return content;
|
|
253
360
|
if (Array.isArray(content)) {
|
|
@@ -351,6 +458,65 @@ function pickFirst(...values) {
|
|
|
351
458
|
return "";
|
|
352
459
|
}
|
|
353
460
|
|
|
461
|
+
function isPlainObject(value) {
|
|
462
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
function isBossGeekDetailShape(value) {
|
|
466
|
+
if (!isPlainObject(value)) return false;
|
|
467
|
+
return Boolean(
|
|
468
|
+
isPlainObject(value.geekBaseInfo)
|
|
469
|
+
|| value.geekName
|
|
470
|
+
|| value.geekAdvantage
|
|
471
|
+
|| Array.isArray(value.geekEduExpList)
|
|
472
|
+
|| Array.isArray(value.geekEducationList)
|
|
473
|
+
|| Array.isArray(value.geekWorkExpList)
|
|
474
|
+
|| Array.isArray(value.geekProjExpList)
|
|
475
|
+
|| Array.isArray(value.geekCertificationList)
|
|
476
|
+
|| Array.isArray(value.geekSkillList)
|
|
477
|
+
|| isPlainObject(value.highestEduExp)
|
|
478
|
+
);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
function isBossChatProfileShape(value) {
|
|
482
|
+
if (!isPlainObject(value)) return false;
|
|
483
|
+
return Boolean(
|
|
484
|
+
(value.name || value.encryptGeekId || value.uid)
|
|
485
|
+
&& (
|
|
486
|
+
Array.isArray(value.eduExpList)
|
|
487
|
+
|| Array.isArray(value.workExpList)
|
|
488
|
+
|| value.school
|
|
489
|
+
|| value.major
|
|
490
|
+
|| value.lastCompany
|
|
491
|
+
|| value.positionName
|
|
492
|
+
)
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
function collectObjects(root, {
|
|
497
|
+
maxObjects = 500,
|
|
498
|
+
maxDepth = 8
|
|
499
|
+
} = {}) {
|
|
500
|
+
if (!root || typeof root !== "object") return [];
|
|
501
|
+
const queue = [{ value: root, depth: 0 }];
|
|
502
|
+
const seen = new WeakSet();
|
|
503
|
+
const objects = [];
|
|
504
|
+
while (queue.length && objects.length < maxObjects) {
|
|
505
|
+
const { value, depth } = queue.shift();
|
|
506
|
+
if (!value || typeof value !== "object" || seen.has(value)) continue;
|
|
507
|
+
seen.add(value);
|
|
508
|
+
if (isPlainObject(value)) objects.push(value);
|
|
509
|
+
if (depth >= maxDepth) continue;
|
|
510
|
+
const children = Array.isArray(value) ? value : Object.values(value);
|
|
511
|
+
for (const child of children) {
|
|
512
|
+
if (child && typeof child === "object") {
|
|
513
|
+
queue.push({ value: child, depth: depth + 1 });
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
return objects;
|
|
518
|
+
}
|
|
519
|
+
|
|
354
520
|
function joinRange(start, end, fallback = "") {
|
|
355
521
|
const left = parseDateLike(start);
|
|
356
522
|
const right = parseDateLike(end);
|
|
@@ -420,8 +586,8 @@ function formatEducation(item = {}, index = 0) {
|
|
|
420
586
|
];
|
|
421
587
|
return [
|
|
422
588
|
`${index + 1}. ${[
|
|
423
|
-
pickFirst(item.school),
|
|
424
|
-
pickFirst(item.major),
|
|
589
|
+
pickFirst(item.school, item.schoolName),
|
|
590
|
+
pickFirst(item.major, item.majorName),
|
|
425
591
|
pickFirst(item.degreeName, item.degree),
|
|
426
592
|
period
|
|
427
593
|
].filter(Boolean).join(" / ")}`,
|
|
@@ -466,16 +632,28 @@ function resolveBossGeekDetail(payload = {}) {
|
|
|
466
632
|
const candidates = [
|
|
467
633
|
{ sourceKey: "geekDetailInfo", detail: payload?.zpData?.geekDetailInfo },
|
|
468
634
|
{ sourceKey: "geekDetail", detail: payload?.zpData?.geekDetail },
|
|
635
|
+
{ sourceKey: "geekDetailInfo", detail: payload?.zpData?.data?.geekDetailInfo },
|
|
636
|
+
{ sourceKey: "geekDetail", detail: payload?.zpData?.data?.geekDetail },
|
|
637
|
+
{ sourceKey: "geekDetailInfo", detail: payload?.zpData?.data?.detailInfo },
|
|
638
|
+
{ sourceKey: "geekDetailInfo", detail: payload?.zpData?.data?.resumeDetail },
|
|
639
|
+
{ sourceKey: "geekDetailInfo", detail: payload?.zpData?.data },
|
|
640
|
+
{ sourceKey: "geekDetailInfo", detail: payload?.data?.geekDetailInfo },
|
|
641
|
+
{ sourceKey: "geekDetail", detail: payload?.data?.geekDetail },
|
|
642
|
+
{ sourceKey: "geekDetailInfo", detail: payload?.data?.detailInfo },
|
|
643
|
+
{ sourceKey: "geekDetailInfo", detail: payload?.data?.resumeDetail },
|
|
644
|
+
{ sourceKey: "geekDetailInfo", detail: payload?.data },
|
|
469
645
|
{ sourceKey: "geekDetailInfo", detail: payload?.geekDetailInfo },
|
|
470
|
-
{ sourceKey: "geekDetail", detail: payload?.geekDetail }
|
|
646
|
+
{ sourceKey: "geekDetail", detail: payload?.geekDetail },
|
|
647
|
+
{ sourceKey: "geekDetailInfo", detail: payload }
|
|
471
648
|
];
|
|
472
|
-
const found = candidates.find((item) => item.detail
|
|
649
|
+
const found = candidates.find((item) => isBossGeekDetailShape(item.detail));
|
|
473
650
|
return found || { sourceKey: "", detail: null };
|
|
474
651
|
}
|
|
475
652
|
|
|
476
653
|
function extractBossChatGeekInfo(payload = {}) {
|
|
477
|
-
const data = payload?.zpData?.data;
|
|
654
|
+
const data = payload?.zpData?.data || payload?.data || payload?.zpData?.geekInfo || payload?.geekInfo;
|
|
478
655
|
if (!data || typeof data !== "object") return null;
|
|
656
|
+
if (!isBossChatProfileShape(data)) return null;
|
|
479
657
|
const educationList = normalizeList(data.eduExpList);
|
|
480
658
|
const workList = normalizeList(data.workExpList);
|
|
481
659
|
const firstEducation = educationList[0] || {};
|
|
@@ -544,7 +722,13 @@ function extractBossChatGeekInfo(payload = {}) {
|
|
|
544
722
|
}
|
|
545
723
|
|
|
546
724
|
function extractBossChatHistoryResume(payload = {}) {
|
|
547
|
-
const messages = normalizeList(payload?.zpData?.messages)
|
|
725
|
+
const messages = normalizeList(payload?.zpData?.messages).length
|
|
726
|
+
? normalizeList(payload?.zpData?.messages)
|
|
727
|
+
: normalizeList(payload?.messages).length
|
|
728
|
+
? normalizeList(payload?.messages)
|
|
729
|
+
: normalizeList(payload?.data?.messages).length
|
|
730
|
+
? normalizeList(payload?.data?.messages)
|
|
731
|
+
: normalizeList(payload?.zpData?.data?.messages);
|
|
548
732
|
const resumes = messages
|
|
549
733
|
.map((message) => message?.body?.resume)
|
|
550
734
|
.filter((resume) => resume && typeof resume === "object");
|
|
@@ -606,12 +790,56 @@ function extractBossChatHistoryResume(payload = {}) {
|
|
|
606
790
|
};
|
|
607
791
|
}
|
|
608
792
|
|
|
793
|
+
function extractBossProfileRecursively(payload = {}) {
|
|
794
|
+
for (const object of collectObjects(payload)) {
|
|
795
|
+
if (isBossGeekDetailShape(object)) {
|
|
796
|
+
const profile = extractBossGeekDetailInfo({ geekDetailInfo: object });
|
|
797
|
+
if (profile?.text || profile?.identity?.name) {
|
|
798
|
+
return {
|
|
799
|
+
...profile,
|
|
800
|
+
source_keys: {
|
|
801
|
+
...(profile.source_keys || {}),
|
|
802
|
+
recursive_profile_match: true
|
|
803
|
+
}
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
if (isBossChatProfileShape(object)) {
|
|
808
|
+
const profile = extractBossChatGeekInfo({ zpData: { data: object } });
|
|
809
|
+
if (profile?.text || profile?.identity?.name) {
|
|
810
|
+
return {
|
|
811
|
+
...profile,
|
|
812
|
+
source_keys: {
|
|
813
|
+
...(profile.source_keys || {}),
|
|
814
|
+
recursive_profile_match: true
|
|
815
|
+
}
|
|
816
|
+
};
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
if (isPlainObject(object.resume)) {
|
|
820
|
+
const profile = extractBossChatHistoryResume({ zpData: { messages: [{ body: { resume: object.resume } }] } });
|
|
821
|
+
if (profile?.text || profile?.identity?.name) {
|
|
822
|
+
return {
|
|
823
|
+
...profile,
|
|
824
|
+
source_keys: {
|
|
825
|
+
...(profile.source_keys || {}),
|
|
826
|
+
recursive_profile_match: true
|
|
827
|
+
}
|
|
828
|
+
};
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
return null;
|
|
833
|
+
}
|
|
834
|
+
|
|
609
835
|
function extractBossGeekDetailInfo(payload = {}) {
|
|
610
836
|
const { sourceKey, detail } = resolveBossGeekDetail(payload);
|
|
611
837
|
if (!detail || typeof detail !== "object") return null;
|
|
612
838
|
|
|
613
|
-
const base = detail.geekBaseInfo || {};
|
|
614
|
-
const educationList = normalizeList(detail.geekEduExpList)
|
|
839
|
+
const base = detail.geekBaseInfo || detail.baseInfo || detail.base || {};
|
|
840
|
+
const educationList = normalizeList(detail.geekEduExpList).length
|
|
841
|
+
? normalizeList(detail.geekEduExpList)
|
|
842
|
+
: normalizeList(detail.geekEducationList);
|
|
615
843
|
const firstEducation = educationList[0] || detail.highestEduExp || {};
|
|
616
844
|
const workList = normalizeList(detail.geekWorkExpList);
|
|
617
845
|
const firstWork = workList[0] || {};
|
|
@@ -625,6 +853,8 @@ function extractBossGeekDetailInfo(payload = {}) {
|
|
|
625
853
|
const normalizedExpectationList = expectationList.length ? expectationList : expectationFallback;
|
|
626
854
|
const certifications = normalizeList(detail.geekCertificationList);
|
|
627
855
|
const skillTags = [
|
|
856
|
+
...normalizeTagList(detail.geekSkillList),
|
|
857
|
+
...normalizeTagList(detail.skillList),
|
|
628
858
|
...normalizeTagList(detail.blueGeekSkills),
|
|
629
859
|
...normalizeTagList(base.userHighlightList),
|
|
630
860
|
...normalizeTagList(base.userDescHighlightList),
|
|
@@ -633,6 +863,7 @@ function extractBossGeekDetailInfo(payload = {}) {
|
|
|
633
863
|
...normalizeTagList(detail.professionalSkill)
|
|
634
864
|
];
|
|
635
865
|
const summaryParts = [
|
|
866
|
+
pickFirst(detail.geekAdvantage),
|
|
636
867
|
pickFirst(base.userDescription),
|
|
637
868
|
pickFirst(base.userDesc),
|
|
638
869
|
pickFirst(base.workEduDesc),
|
|
@@ -640,7 +871,7 @@ function extractBossGeekDetailInfo(payload = {}) {
|
|
|
640
871
|
].filter(Boolean);
|
|
641
872
|
const sections = {
|
|
642
873
|
base: [
|
|
643
|
-
base.name ? `姓名:${
|
|
874
|
+
pickFirst(base.name, detail.geekName, detail.name) ? `姓名:${pickFirst(base.name, detail.geekName, detail.name)}` : "",
|
|
644
875
|
normalizeGenderValue(base.gender) ? `性别:${normalizeGenderValue(base.gender)}` : "",
|
|
645
876
|
pickFirst(base.ageDesc, base.age) ? `年龄:${pickFirst(base.ageDesc, base.age)}` : "",
|
|
646
877
|
pickFirst(base.degreeCategory) ? `最高学历:${pickFirst(base.degreeCategory)}` : "",
|
|
@@ -669,11 +900,11 @@ function extractBossGeekDetailInfo(payload = {}) {
|
|
|
669
900
|
|
|
670
901
|
return {
|
|
671
902
|
identity: {
|
|
672
|
-
name: pickFirst(base.name),
|
|
903
|
+
name: pickFirst(base.name, detail.geekName, detail.name),
|
|
673
904
|
current_position: pickFirst(firstWork.positionName, firstWork.positionTitle, firstWork.position),
|
|
674
|
-
current_company: pickFirst(firstWork.formattedCompany, firstWork.company),
|
|
675
|
-
school: pickFirst(firstEducation.school),
|
|
676
|
-
major: pickFirst(firstEducation.major),
|
|
905
|
+
current_company: pickFirst(firstWork.formattedCompany, firstWork.company, firstWork.brandName),
|
|
906
|
+
school: pickFirst(firstEducation.school, firstEducation.schoolName),
|
|
907
|
+
major: pickFirst(firstEducation.major, firstEducation.majorName),
|
|
677
908
|
degree: pickFirst(base.degreeCategory, firstEducation.degreeName, firstEducation.degree),
|
|
678
909
|
years_experience: parseYearsExperience(pickFirst(base.workYearDesc, base.workYearsDesc)) ?? null,
|
|
679
910
|
age: parseAge(pickFirst(base.ageDesc, base.age)) ?? null,
|
|
@@ -703,24 +934,68 @@ function extractBossGeekDetailInfo(payload = {}) {
|
|
|
703
934
|
|
|
704
935
|
export function extractBossProfileFromNetworkBody(networkBody = {}) {
|
|
705
936
|
const text = parseNetworkBodyText(networkBody);
|
|
706
|
-
const
|
|
707
|
-
|
|
937
|
+
const parsedObjects = [
|
|
938
|
+
tryParseJson(text),
|
|
939
|
+
...tryParseEmbeddedJsonObjects(text)
|
|
940
|
+
].filter((item) => item && typeof item === "object");
|
|
941
|
+
if (!parsedObjects.length) {
|
|
942
|
+
const htmlText = /<html|<body|<div|<section|<script/i.test(text) ? htmlToText(text) : "";
|
|
943
|
+
if (htmlText && htmlText.length > 80) {
|
|
944
|
+
const candidate = normalizeCandidateProfile({
|
|
945
|
+
domain: "recommend",
|
|
946
|
+
source: "network-html-fallback",
|
|
947
|
+
text: htmlText
|
|
948
|
+
});
|
|
949
|
+
return {
|
|
950
|
+
ok: true,
|
|
951
|
+
url: networkBody.url || null,
|
|
952
|
+
status: networkBody.status ?? null,
|
|
953
|
+
mimeType: networkBody.mimeType || null,
|
|
954
|
+
text_length: text.length,
|
|
955
|
+
profile: {
|
|
956
|
+
identity: candidate.identity,
|
|
957
|
+
tags: candidate.tags,
|
|
958
|
+
sections: { html_text: [htmlText] },
|
|
959
|
+
text: htmlText,
|
|
960
|
+
source_keys: {
|
|
961
|
+
network_html_text: true,
|
|
962
|
+
html_text_length: htmlText.length
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
};
|
|
966
|
+
}
|
|
708
967
|
return {
|
|
709
968
|
ok: false,
|
|
710
969
|
error: "NETWORK_BODY_NOT_JSON",
|
|
711
970
|
text_length: text.length
|
|
712
971
|
};
|
|
713
972
|
}
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
973
|
+
let profile = null;
|
|
974
|
+
let parsed = parsedObjects[0];
|
|
975
|
+
for (const candidateObject of parsedObjects) {
|
|
976
|
+
profile = extractBossGeekDetailInfo(candidateObject)
|
|
977
|
+
|| extractBossChatGeekInfo(candidateObject)
|
|
978
|
+
|| extractBossChatHistoryResume(candidateObject)
|
|
979
|
+
|| extractBossProfileRecursively(candidateObject);
|
|
980
|
+
if (profile) {
|
|
981
|
+
parsed = candidateObject;
|
|
982
|
+
break;
|
|
983
|
+
}
|
|
984
|
+
}
|
|
717
985
|
if (!profile) {
|
|
986
|
+
const encryptedPayload = parsedObjects.find((item) => (
|
|
987
|
+
normalizeText(item?.zpData?.encryptGeekDetailInfo || item?.encryptGeekDetailInfo || "")
|
|
988
|
+
));
|
|
718
989
|
return {
|
|
719
990
|
ok: false,
|
|
720
|
-
error: "BOSS_GEEK_DETAIL_INFO_NOT_FOUND",
|
|
991
|
+
error: encryptedPayload ? "BOSS_GEEK_DETAIL_INFO_ENCRYPTED" : "BOSS_GEEK_DETAIL_INFO_NOT_FOUND",
|
|
721
992
|
text_length: text.length,
|
|
722
|
-
|
|
723
|
-
|
|
993
|
+
parsed_object_count: parsedObjects.length,
|
|
994
|
+
top_level_keys: Object.keys(parsed || {}).slice(0, 30),
|
|
995
|
+
zpData_keys: Object.keys(parsed?.zpData || {}).slice(0, 50),
|
|
996
|
+
data_keys: Object.keys(parsed?.data || parsed?.zpData?.data || {}).slice(0, 50),
|
|
997
|
+
encrypted_resume: Boolean(encryptedPayload),
|
|
998
|
+
encrypted_resume_length: normalizeText(encryptedPayload?.zpData?.encryptGeekDetailInfo || encryptedPayload?.encryptGeekDetailInfo || "").length
|
|
724
999
|
};
|
|
725
1000
|
}
|
|
726
1001
|
return {
|
|
@@ -834,7 +1109,8 @@ export function normalizeCandidateProfile(input = {}) {
|
|
|
834
1109
|
|| attrs.href
|
|
835
1110
|
|| ""
|
|
836
1111
|
) || null;
|
|
837
|
-
const
|
|
1112
|
+
const explicitName = cleanInferredNameLine(input.identity?.name || input.name || "");
|
|
1113
|
+
const inferredName = explicitName || firstUsefulLine(lines) || null;
|
|
838
1114
|
const fullText = collectTextParts({
|
|
839
1115
|
...input,
|
|
840
1116
|
text: rawText,
|
|
@@ -1046,6 +1322,8 @@ export function createFailedLlmScreeningResult(error) {
|
|
|
1046
1322
|
decision_cot: "",
|
|
1047
1323
|
reasoning_content: "",
|
|
1048
1324
|
raw_model_output: "",
|
|
1325
|
+
image_input_count: Number(error?.image_input_count) || 0,
|
|
1326
|
+
image_inputs: Array.isArray(error?.image_inputs) ? error.image_inputs : [],
|
|
1049
1327
|
error: error?.message || String(error || "unknown"),
|
|
1050
1328
|
screened_at: nowIso()
|
|
1051
1329
|
};
|
|
@@ -1134,6 +1412,7 @@ export async function callScreeningLlm({
|
|
|
1134
1412
|
const payload = {
|
|
1135
1413
|
model,
|
|
1136
1414
|
temperature: 0.1,
|
|
1415
|
+
max_tokens: Math.max(1, Number(config.maxTokens || config.llmMaxTokens) || 64),
|
|
1137
1416
|
messages: buildScreeningLlmMessages({
|
|
1138
1417
|
candidate,
|
|
1139
1418
|
criteria,
|
|
@@ -1143,7 +1422,7 @@ export async function callScreeningLlm({
|
|
|
1143
1422
|
applyChatCompletionThinking(payload, {
|
|
1144
1423
|
baseUrl,
|
|
1145
1424
|
model,
|
|
1146
|
-
thinkingLevel: config.llmThinkingLevel || config.thinkingLevel || config.reasoningEffort
|
|
1425
|
+
thinkingLevel: config.llmThinkingLevel || config.thinkingLevel || config.reasoningEffort || "off"
|
|
1147
1426
|
});
|
|
1148
1427
|
|
|
1149
1428
|
const controller = new AbortController();
|
|
@@ -1208,6 +1487,10 @@ export async function callScreeningLlm({
|
|
|
1208
1487
|
image_inputs: summarizeLlmImageInputs(imageInputs),
|
|
1209
1488
|
screened_at: nowIso()
|
|
1210
1489
|
};
|
|
1490
|
+
} catch (error) {
|
|
1491
|
+
error.image_input_count = imageInputs.length;
|
|
1492
|
+
error.image_inputs = summarizeLlmImageInputs(imageInputs);
|
|
1493
|
+
throw error;
|
|
1211
1494
|
} finally {
|
|
1212
1495
|
clearTimeout(timer);
|
|
1213
1496
|
}
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
querySelectorAll,
|
|
6
6
|
sleep
|
|
7
7
|
} from "../../core/browser/index.js";
|
|
8
|
+
import { mergeBossCandidateCardFields } from "../../core/boss-cards/index.js";
|
|
8
9
|
import {
|
|
9
10
|
htmlToText,
|
|
10
11
|
normalizeCandidateProfile,
|
|
@@ -24,6 +25,12 @@ function firstCandidateId(attributes = {}) {
|
|
|
24
25
|
) || null;
|
|
25
26
|
}
|
|
26
27
|
|
|
28
|
+
function mergeChatCardFields(candidate, outerHTML = "") {
|
|
29
|
+
return mergeBossCandidateCardFields(candidate, outerHTML, {
|
|
30
|
+
metadataKey: "chat_card_fields"
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
27
34
|
export async function findChatCandidateNodeIds(client, rootNodeId, {
|
|
28
35
|
selectors = CHAT_CARD_SELECTORS
|
|
29
36
|
} = {}) {
|
|
@@ -97,7 +104,7 @@ export async function readChatCardCandidate(client, cardNodeId, {
|
|
|
97
104
|
getAttributesMap(client, cardNodeId),
|
|
98
105
|
getOuterHTML(client, cardNodeId)
|
|
99
106
|
]);
|
|
100
|
-
|
|
107
|
+
const candidate = normalizeCandidateProfile({
|
|
101
108
|
domain: "chat",
|
|
102
109
|
source,
|
|
103
110
|
id: firstCandidateId(attributes),
|
|
@@ -110,6 +117,7 @@ export async function readChatCardCandidate(client, cardNodeId, {
|
|
|
110
117
|
...metadata
|
|
111
118
|
}
|
|
112
119
|
});
|
|
120
|
+
return mergeChatCardFields(candidate, outerHTML);
|
|
113
121
|
}
|
|
114
122
|
|
|
115
123
|
export async function readFirstChatCardCandidate(client, rootNodeId, options = {}) {
|
|
@@ -1273,10 +1273,10 @@ export async function extractChatProfileCandidate(client, {
|
|
|
1273
1273
|
resumeHtml: providedResumeHtml = null,
|
|
1274
1274
|
networkEvents = [],
|
|
1275
1275
|
targetUrl = "",
|
|
1276
|
-
closeResume = true
|
|
1276
|
+
closeResume = true,
|
|
1277
|
+
networkParseRetryMs = 1800,
|
|
1278
|
+
networkParseIntervalMs = 250
|
|
1277
1279
|
} = {}) {
|
|
1278
|
-
await sleep(1000);
|
|
1279
|
-
const networkBodies = await readChatProfileNetworkBodies(client, networkEvents);
|
|
1280
1280
|
let resumeHtml = providedResumeHtml || null;
|
|
1281
1281
|
if (!resumeHtml) {
|
|
1282
1282
|
try {
|
|
@@ -1292,21 +1292,30 @@ export async function extractChatProfileCandidate(client, {
|
|
|
1292
1292
|
resumeHtml.resumeIframeText
|
|
1293
1293
|
].filter(Boolean).join("\n\n");
|
|
1294
1294
|
|
|
1295
|
-
const
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1295
|
+
const parseStarted = Date.now();
|
|
1296
|
+
let networkBodies = [];
|
|
1297
|
+
let detailCandidateResult = null;
|
|
1298
|
+
do {
|
|
1299
|
+
networkBodies = await readChatProfileNetworkBodies(client, networkEvents);
|
|
1300
|
+
detailCandidateResult = buildScreeningCandidateFromDetail({
|
|
1301
|
+
domain: "chat",
|
|
1302
|
+
source: "chat-live-cdp-profile",
|
|
1303
|
+
cardCandidate,
|
|
1304
|
+
detailText,
|
|
1305
|
+
networkBodies,
|
|
1306
|
+
metadata: {
|
|
1307
|
+
target_url: targetUrl,
|
|
1308
|
+
card_node_id: cardNodeId,
|
|
1309
|
+
resume_popup_selector: resumeState?.popup?.selector || null,
|
|
1310
|
+
resume_content_selector: resumeState?.content?.selector || null,
|
|
1311
|
+
resume_iframe_selector: resumeState?.resumeIframe?.selector || null,
|
|
1312
|
+
resume_iframe_document_node_id: resumeHtml.resumeIframeDocumentNodeId
|
|
1313
|
+
}
|
|
1314
|
+
});
|
|
1315
|
+
if (detailCandidateResult.parsed_network_profiles.some((item) => item.ok)) break;
|
|
1316
|
+
if (Date.now() - parseStarted >= Math.max(0, Number(networkParseRetryMs) || 0)) break;
|
|
1317
|
+
await sleep(Math.max(50, Number(networkParseIntervalMs) || 250));
|
|
1318
|
+
} while (true);
|
|
1310
1319
|
|
|
1311
1320
|
let closeResult = null;
|
|
1312
1321
|
if (closeResume) {
|
|
@@ -1317,6 +1326,8 @@ export async function extractChatProfileCandidate(client, {
|
|
|
1317
1326
|
candidate: detailCandidateResult.candidate,
|
|
1318
1327
|
parsed_network_profiles: detailCandidateResult.parsed_network_profiles,
|
|
1319
1328
|
network_bodies: networkBodies,
|
|
1329
|
+
network_parse_retry_elapsed_ms: Date.now() - parseStarted,
|
|
1330
|
+
network_event_count: networkEvents.length,
|
|
1320
1331
|
detail: {
|
|
1321
1332
|
popup_text: resumeHtml.popupText,
|
|
1322
1333
|
content_text: resumeHtml.contentText,
|