@reconcrap/boss-recommend-mcp 1.3.19 → 1.3.20

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 CHANGED
@@ -167,7 +167,8 @@ config/screening-config.example.json
167
167
  - `openaiProject`
168
168
  - `debugPort`
169
169
  - `outputDir`
170
- - `llmThinkingLevel`:默认 `off`。可设为 `off/minimal/low/medium/high/auto/current`,用于控制 OpenAI-compatible LLM 的 thinking/reasoning 强度。
170
+ - `llmThinkingLevel`:默认 `low`。可设为 `off/minimal/low/medium/high/auto/current`,用于控制 OpenAI-compatible LLM 的 thinking/reasoning 强度。
171
+ - `humanRestEnabled`:默认 `false`。`false` 时 recommend-screen 随机休息/批次休息与 boss-chat 批次休息均为 `0ms`;`true` 时恢复随机休息节奏。
171
172
 
172
173
  ## 常用命令
173
174
 
@@ -2,7 +2,8 @@
2
2
  "baseUrl": "https://api.openai.com/v1",
3
3
  "apiKey": "replace-with-openai-api-key",
4
4
  "model": "gpt-4.1-mini",
5
- "llmThinkingLevel": "off",
5
+ "llmThinkingLevel": "low",
6
+ "humanRestEnabled": false,
6
7
  "openaiOrganization": "optional-org-id",
7
8
  "openaiProject": "optional-project-id"
8
9
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reconcrap/boss-recommend-mcp",
3
- "version": "1.3.19",
3
+ "version": "1.3.20",
4
4
  "description": "Unified MCP pipeline for recommend-page filtering and screening on Boss Zhipin",
5
5
  "keywords": [
6
6
  "boss",
package/src/adapters.js CHANGED
@@ -121,6 +121,30 @@ function normalizeText(value) {
121
121
  return String(value || "").replace(/\s+/g, " ").trim();
122
122
  }
123
123
 
124
+ function parseBooleanValue(value) {
125
+ if (typeof value === "boolean") return value;
126
+ const normalized = normalizeText(value).toLowerCase();
127
+ if (!normalized) return null;
128
+ if (["1", "true", "yes", "y", "on", "是"].includes(normalized)) return true;
129
+ if (["0", "false", "no", "n", "off", "否"].includes(normalized)) return false;
130
+ return null;
131
+ }
132
+
133
+ function resolveHumanRestEnabled(config = {}) {
134
+ if (!config || typeof config !== "object" || Array.isArray(config)) return false;
135
+ const candidates = [
136
+ config.humanRestEnabled,
137
+ config.human_rest_enabled,
138
+ config.humanLikeRestEnabled,
139
+ config.human_like_rest_enabled
140
+ ];
141
+ for (const candidate of candidates) {
142
+ const parsed = parseBooleanValue(candidate);
143
+ if (typeof parsed === "boolean") return parsed;
144
+ }
145
+ return false;
146
+ }
147
+
124
148
  function serializeInputSummary(value) {
125
149
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
126
150
  try {
@@ -2937,6 +2961,7 @@ export async function runRecommendScreenCli({
2937
2961
  if (llmThinkingLevel) {
2938
2962
  args.push("--thinking-level", llmThinkingLevel);
2939
2963
  }
2964
+ args.push("--human-rest", String(resolveHumanRestEnabled(loaded.config)));
2940
2965
  if (Number.isInteger(screenParams.target_count) && screenParams.target_count > 0) {
2941
2966
  args.push("--targetCount", String(screenParams.target_count));
2942
2967
  }
package/src/boss-chat.js CHANGED
@@ -40,6 +40,30 @@ function parsePositiveInteger(value, fallback = null) {
40
40
  return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
41
41
  }
42
42
 
43
+ function parseBooleanValue(value) {
44
+ if (typeof value === "boolean") return value;
45
+ const normalized = normalizeText(value).toLowerCase();
46
+ if (!normalized) return null;
47
+ if (["1", "true", "yes", "y", "on", "是"].includes(normalized)) return true;
48
+ if (["0", "false", "no", "n", "off", "否"].includes(normalized)) return false;
49
+ return null;
50
+ }
51
+
52
+ function resolveHumanRestEnabled(config = {}) {
53
+ if (!config || typeof config !== "object" || Array.isArray(config)) return false;
54
+ const candidates = [
55
+ config.humanRestEnabled,
56
+ config.human_rest_enabled,
57
+ config.humanLikeRestEnabled,
58
+ config.human_like_rest_enabled
59
+ ];
60
+ for (const candidate of candidates) {
61
+ const parsed = parseBooleanValue(candidate);
62
+ if (typeof parsed === "boolean") return parsed;
63
+ }
64
+ return false;
65
+ }
66
+
43
67
  function isUnlimitedTargetCountToken(value) {
44
68
  const token = normalizeText(value).toLowerCase();
45
69
  if (!token) return false;
@@ -290,7 +314,8 @@ function resolveBossChatScreenConfig(workspaceRoot) {
290
314
  apiKey: normalizeText(parsed.apiKey),
291
315
  model: normalizeText(parsed.model),
292
316
  llmThinkingLevel: resolveLlmThinkingLevel(parsed),
293
- debugPort: parsePositiveInteger(parsed.debugPort, 9222)
317
+ debugPort: parsePositiveInteger(parsed.debugPort, 9222),
318
+ humanRestEnabled: resolveHumanRestEnabled(parsed)
294
319
  },
295
320
  config_path: configPath,
296
321
  config_dir: path.dirname(configPath)
@@ -430,6 +455,8 @@ function buildBossChatCliArgs(command, input, resolvedConfig) {
430
455
  }
431
456
  if (typeof normalized.batchRestEnabled === "boolean") {
432
457
  args.push("--batch-rest", String(normalized.batchRestEnabled));
458
+ } else if (typeof resolvedConfig?.humanRestEnabled === "boolean") {
459
+ args.push("--batch-rest", String(resolvedConfig.humanRestEnabled));
433
460
  }
434
461
  return args;
435
462
  }
@@ -755,8 +755,8 @@ async function testBossChatLlmShouldApplyThinkingDefaultsAndOverrides() {
755
755
  },
756
756
  });
757
757
  await volcClient.requestCompletions({ prompt: "prompt", evidenceCorpus: "resume" });
758
- assert.deepEqual(volcCompletionPayload.thinking, { type: "disabled" });
759
- assert.equal(volcCompletionPayload.reasoning_effort, "minimal");
758
+ assert.deepEqual(volcCompletionPayload.thinking, { type: "enabled" });
759
+ assert.equal(volcCompletionPayload.reasoning_effort, "low");
760
760
 
761
761
  let lowCompletionPayload = null;
762
762
  const lowClient = new LlmClient({
@@ -787,7 +787,7 @@ async function testBossChatLlmShouldApplyThinkingDefaultsAndOverrides() {
787
787
  });
788
788
  await openaiClient.requestCompletions({ prompt: "prompt", evidenceCorpus: "resume" });
789
789
  assert.equal(openaiCompletionPayload.thinking, undefined);
790
- assert.equal(openaiCompletionPayload.reasoning_effort, "minimal");
790
+ assert.equal(openaiCompletionPayload.reasoning_effort, "low");
791
791
 
792
792
  let responsesPayload = null;
793
793
  const responsesClient = new LlmClient({
@@ -47,7 +47,7 @@ export class InteractionController {
47
47
  return 0;
48
48
  }
49
49
 
50
- const restMs = 0;
50
+ const restMs = 4000 + Math.floor(Math.random() * 4000);
51
51
  logger.log(`短暂休息 ${restMs}ms,保持处理节奏稳定...`);
52
52
  await this.wait(restMs);
53
53
  this.nextRestAt = processedCount + this.randomRestThreshold();
@@ -104,7 +104,7 @@ function resolveLlmThinkingLevel(config = {}, options = {}) {
104
104
  normalizeLlmThinkingLevel(config.reasoningEffort) ||
105
105
  normalizeLlmThinkingLevel(config.reasoning_effort) ||
106
106
  getEnvLlmThinkingLevel() ||
107
- 'off'
107
+ 'low'
108
108
  );
109
109
  }
110
110
 
@@ -118,7 +118,7 @@ function isVolcengineModel(baseUrl, model) {
118
118
  }
119
119
 
120
120
  function applyChatCompletionThinking(payload, { baseUrl = '', model = '', thinkingLevel = '' } = {}) {
121
- const level = normalizeLlmThinkingLevel(thinkingLevel) || 'off';
121
+ const level = normalizeLlmThinkingLevel(thinkingLevel) || 'low';
122
122
  if (isProviderDefaultThinkingLevel(level)) return payload;
123
123
  const isVolc = isVolcengineModel(baseUrl, model);
124
124
  if (isVolc) {
@@ -142,7 +142,7 @@ function applyChatCompletionThinking(payload, { baseUrl = '', model = '', thinki
142
142
  }
143
143
 
144
144
  function applyResponsesThinking(payload, { thinkingLevel = '' } = {}) {
145
- const level = normalizeLlmThinkingLevel(thinkingLevel) || 'off';
145
+ const level = normalizeLlmThinkingLevel(thinkingLevel) || 'low';
146
146
  if (isProviderDefaultThinkingLevel(level) || level === 'auto') return payload;
147
147
  payload.reasoning = {
148
148
  ...(payload.reasoning || {}),
@@ -1288,7 +1288,7 @@ function getEnvLlmThinkingLevel() {
1288
1288
  }
1289
1289
 
1290
1290
  function resolveLlmThinkingLevel(value) {
1291
- return normalizeLlmThinkingLevel(value) || getEnvLlmThinkingLevel() || "off";
1291
+ return normalizeLlmThinkingLevel(value) || getEnvLlmThinkingLevel() || "low";
1292
1292
  }
1293
1293
 
1294
1294
  function isVolcengineModel(baseUrl, model) {
@@ -1339,6 +1339,7 @@ function parseArgs(argv) {
1339
1339
  checkpointPath: null,
1340
1340
  pauseControlPath: null,
1341
1341
  resume: false,
1342
+ humanRestEnabled: false,
1342
1343
  postAction: null,
1343
1344
  postActionConfirmed: null,
1344
1345
  help: false,
@@ -1353,6 +1354,7 @@ function parseArgs(argv) {
1353
1354
  pageScope: false,
1354
1355
  calibrationPath: false,
1355
1356
  port: false,
1357
+ humanRest: false,
1356
1358
  postAction: false,
1357
1359
  postActionConfirmed: false
1358
1360
  }
@@ -1421,6 +1423,11 @@ function parseArgs(argv) {
1421
1423
  } else if (token === "--pause-control-path" && (inlineValue || next)) {
1422
1424
  parsed.pauseControlPath = path.resolve(inlineValue || next);
1423
1425
  if (!inlineValue) index += 1;
1426
+ } else if ((token === "--human-rest" || token === "--humanRest" || token === "--human_rest") && (inlineValue || next)) {
1427
+ const parsedBoolean = parseBoolean(inlineValue || next);
1428
+ parsed.humanRestEnabled = parsedBoolean === true;
1429
+ parsed.__provided.humanRest = parsedBoolean !== null;
1430
+ if (!inlineValue) index += 1;
1424
1431
  } else if (token === "--resume") {
1425
1432
  parsed.resume = true;
1426
1433
  } else if ((token === "--post-action" || token === "--postAction") && (inlineValue || next)) {
@@ -5743,12 +5750,12 @@ class RecommendScreenCli {
5743
5750
  async takeBreakIfNeeded() {
5744
5751
  this.restCounter += 1;
5745
5752
  if (Math.random() < 0.08) {
5746
- const pauseMs = 0;
5753
+ const pauseMs = this.args.humanRestEnabled ? 3000 + Math.floor(Math.random() * 4000) : 0;
5747
5754
  log(`[随机休息] 暂停 ${Math.round(pauseMs / 1000)} 秒`);
5748
5755
  await sleep(pauseMs);
5749
5756
  }
5750
5757
  if (this.restCounter >= this.restThreshold) {
5751
- const pauseMs = 0;
5758
+ const pauseMs = this.args.humanRestEnabled ? 15000 + Math.floor(Math.random() * 15000) : 0;
5752
5759
  log(`[批次休息] 已连续处理 ${this.restCounter} 人,暂停 ${Math.round(pauseMs / 1000)} 秒`);
5753
5760
  await sleep(pauseMs);
5754
5761
  this.restCounter = 0;
@@ -6469,7 +6476,7 @@ async function main() {
6469
6476
  console.log(JSON.stringify({
6470
6477
  status: "COMPLETED",
6471
6478
  result: {
6472
- usage: "node boss-recommend-screen-cli.cjs --criteria \"有 MCP 开发经验\" --post-action <favorite|greet|none> --max-greet-count 10 --post-action-confirmed true --baseurl <url> --apikey <key> --model <model> --thinking-level off|low|medium|high|current --page-scope recommend|latest|featured --calibration <favorite-calibration.json> --port 9222 --output <csv-path> [--input-summary-json <json>] --checkpoint-path <checkpoint.json> --pause-control-path <pause-control.json> [--resume]"
6479
+ usage: "node boss-recommend-screen-cli.cjs --criteria \"有 MCP 开发经验\" --post-action <favorite|greet|none> --max-greet-count 10 --post-action-confirmed true --baseurl <url> --apikey <key> --model <model> --thinking-level off|low|medium|high|current --page-scope recommend|latest|featured --calibration <favorite-calibration.json> --port 9222 --human-rest <true|false> --output <csv-path> [--input-summary-json <json>] --checkpoint-path <checkpoint.json> --pause-control-path <pause-control.json> [--resume]"
6473
6480
  }
6474
6481
  }));
6475
6482
  return;
@@ -1525,8 +1525,8 @@ async function testCallTextModelShouldFallbackToChunkModeOnContextLimit() {
1525
1525
  }
1526
1526
  }
1527
1527
 
1528
- async function testTextModelShouldDefaultThinkingOffForVolcengine() {
1529
- const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "boss-recommend-thinking-off-"));
1528
+ async function testTextModelShouldDefaultThinkingLowForVolcengine() {
1529
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "boss-recommend-thinking-low-default-"));
1530
1530
  const cli = new RecommendScreenCli(createArgs(tempDir));
1531
1531
  cli.args.baseUrl = "https://ark.cn-beijing.volces.com/api/v3";
1532
1532
  cli.args.model = "doubao-seed-2-0-mini-260215";
@@ -1552,8 +1552,8 @@ async function testTextModelShouldDefaultThinkingOffForVolcengine() {
1552
1552
  };
1553
1553
  try {
1554
1554
  await cli.callTextModel("resume");
1555
- assert.deepEqual(capturedPayload?.thinking, { type: "disabled" });
1556
- assert.equal(capturedPayload?.reasoning_effort, "minimal");
1555
+ assert.deepEqual(capturedPayload?.thinking, { type: "enabled" });
1556
+ assert.equal(capturedPayload?.reasoning_effort, "low");
1557
1557
  } finally {
1558
1558
  global.fetch = originalFetch;
1559
1559
  }
@@ -1812,7 +1812,7 @@ async function main() {
1812
1812
  testParseArgsShouldSupportInputSummaryJson();
1813
1813
  await testCallTextModelShouldNotTruncateLongResume();
1814
1814
  await testCallTextModelShouldFallbackToChunkModeOnContextLimit();
1815
- await testTextModelShouldDefaultThinkingOffForVolcengine();
1815
+ await testTextModelShouldDefaultThinkingLowForVolcengine();
1816
1816
  await testTextModelShouldSupportLowThinkingForVolcengine();
1817
1817
  await testPrepareVisionImageSegmentsShouldSplitLongImage();
1818
1818
  await testVisionEvidenceGateShouldDemoteImageFallbackWithoutEvidence();