@reconcrap/boss-recommend-mcp 1.3.16 → 1.3.18
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 -1
- package/config/screening-config.example.json +1 -0
- package/package.json +1 -1
- package/src/adapters.js +28 -9
- package/src/boss-chat.js +22 -0
- package/src/cli.js +6 -1
- package/src/test-boss-chat.js +89 -0
- package/vendor/boss-chat-cli/src/cli.js +10 -0
- package/vendor/boss-chat-cli/src/mcp/server.js +1 -1
- package/vendor/boss-chat-cli/src/mcp/tool-runtime.js +1 -1
- package/vendor/boss-chat-cli/src/services/llm.js +119 -24
- package/vendor/boss-chat-cli/src/services/profile-store.js +5 -0
- package/vendor/boss-recommend-screen-cli/boss-recommend-screen-cli.cjs +597 -82
- package/vendor/boss-recommend-screen-cli/scripts/capture-full-resume-canvas.cjs +109 -41
- package/vendor/boss-recommend-screen-cli/test-recoverable-resume-failures.cjs +259 -0
package/README.md
CHANGED
|
@@ -167,6 +167,7 @@ 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
171
|
|
|
171
172
|
## 常用命令
|
|
172
173
|
|
|
@@ -175,7 +176,7 @@ npm 包安装后可直接使用可执行命令 `boss-recommend-mcp`。以下示
|
|
|
175
176
|
```bash
|
|
176
177
|
node src/cli.js install --agent trae-cn
|
|
177
178
|
node src/cli.js init-config
|
|
178
|
-
node src/cli.js config set --base-url https://api.openai.com/v1 --api-key <your-key> --model gpt-4o-mini
|
|
179
|
+
node src/cli.js config set --base-url https://api.openai.com/v1 --api-key <your-key> --model gpt-4o-mini --thinking-level off
|
|
179
180
|
node src/cli.js set-port --port 9222
|
|
180
181
|
node src/cli.js doctor --agent trae-cn
|
|
181
182
|
node src/cli.js launch-chrome --port 9222
|
package/package.json
CHANGED
package/src/adapters.js
CHANGED
|
@@ -21,6 +21,12 @@ const screenConfigTemplateDefaults = {
|
|
|
21
21
|
apiKey: "replace-with-openai-api-key",
|
|
22
22
|
model: "gpt-4.1-mini"
|
|
23
23
|
};
|
|
24
|
+
const LLM_THINKING_LEVEL_FIELDS = [
|
|
25
|
+
"llmThinkingLevel",
|
|
26
|
+
"thinkingLevel",
|
|
27
|
+
"reasoningEffort",
|
|
28
|
+
"reasoning_effort"
|
|
29
|
+
];
|
|
24
30
|
const DEFAULT_RECOMMEND_SCREEN_TIMEOUT_MS = 24 * 60 * 60 * 1000;
|
|
25
31
|
const PAGE_SCOPE_TO_TAB_STATUS = {
|
|
26
32
|
recommend: "0",
|
|
@@ -324,7 +330,7 @@ function readJsonFile(filePath) {
|
|
|
324
330
|
}
|
|
325
331
|
}
|
|
326
332
|
|
|
327
|
-
function validateScreenConfig(config) {
|
|
333
|
+
function validateScreenConfig(config) {
|
|
328
334
|
if (!config || typeof config !== "object" || Array.isArray(config)) {
|
|
329
335
|
return {
|
|
330
336
|
ok: false,
|
|
@@ -364,8 +370,17 @@ function validateScreenConfig(config) {
|
|
|
364
370
|
message: "screening-config.json 仍是默认模板值,请填写 baseUrl、apiKey、model。"
|
|
365
371
|
};
|
|
366
372
|
}
|
|
367
|
-
return { ok: true, reason: "OK", message: "screening-config.json 校验通过。" };
|
|
368
|
-
}
|
|
373
|
+
return { ok: true, reason: "OK", message: "screening-config.json 校验通过。" };
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function resolveLlmThinkingLevel(config = {}) {
|
|
377
|
+
if (!config || typeof config !== "object") return "";
|
|
378
|
+
for (const field of LLM_THINKING_LEVEL_FIELDS) {
|
|
379
|
+
const value = String(config[field] ?? "").trim();
|
|
380
|
+
if (value) return value;
|
|
381
|
+
}
|
|
382
|
+
return "";
|
|
383
|
+
}
|
|
369
384
|
|
|
370
385
|
function resolveWorkspaceDebugPort(workspaceRoot) {
|
|
371
386
|
const fromEnv = parsePositiveInteger(process.env.BOSS_RECOMMEND_CHROME_PORT);
|
|
@@ -2915,12 +2930,16 @@ export async function runRecommendScreenCli({
|
|
|
2915
2930
|
if (loaded.config.openaiOrganization) {
|
|
2916
2931
|
args.push("--openai-organization", loaded.config.openaiOrganization);
|
|
2917
2932
|
}
|
|
2918
|
-
if (loaded.config.openaiProject) {
|
|
2919
|
-
args.push("--openai-project", loaded.config.openaiProject);
|
|
2920
|
-
}
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2933
|
+
if (loaded.config.openaiProject) {
|
|
2934
|
+
args.push("--openai-project", loaded.config.openaiProject);
|
|
2935
|
+
}
|
|
2936
|
+
const llmThinkingLevel = resolveLlmThinkingLevel(loaded.config);
|
|
2937
|
+
if (llmThinkingLevel) {
|
|
2938
|
+
args.push("--thinking-level", llmThinkingLevel);
|
|
2939
|
+
}
|
|
2940
|
+
if (Number.isInteger(screenParams.target_count) && screenParams.target_count > 0) {
|
|
2941
|
+
args.push("--targetCount", String(screenParams.target_count));
|
|
2942
|
+
}
|
|
2924
2943
|
if (screenParams.post_action === "greet"
|
|
2925
2944
|
&& Number.isInteger(screenParams.max_greet_count)
|
|
2926
2945
|
&& screenParams.max_greet_count > 0) {
|
package/src/boss-chat.js
CHANGED
|
@@ -16,6 +16,12 @@ const BOSS_CHAT_TERMINAL_STATES = new Set(["completed", "failed", "canceled"]);
|
|
|
16
16
|
const CHAT_REQUIRED_FIELDS = ["job", "start_from", "target_count", "criteria"];
|
|
17
17
|
export const TARGET_COUNT_ACCEPTED_EXAMPLES = ["all", -1, 20, "全部候选人"];
|
|
18
18
|
const TARGET_COUNT_WRAPPER_KEYS = ["target_count", "targetCount", "value", "count", "limit"];
|
|
19
|
+
const LLM_THINKING_LEVEL_FIELDS = [
|
|
20
|
+
"llmThinkingLevel",
|
|
21
|
+
"thinkingLevel",
|
|
22
|
+
"reasoningEffort",
|
|
23
|
+
"reasoning_effort"
|
|
24
|
+
];
|
|
19
25
|
|
|
20
26
|
function normalizeText(value) {
|
|
21
27
|
return String(value || "").replace(/\s+/g, " ").trim();
|
|
@@ -228,6 +234,15 @@ function validateRecommendScreenConfig(config) {
|
|
|
228
234
|
return { ok: true };
|
|
229
235
|
}
|
|
230
236
|
|
|
237
|
+
function resolveLlmThinkingLevel(config = {}) {
|
|
238
|
+
if (!config || typeof config !== "object") return "";
|
|
239
|
+
for (const field of LLM_THINKING_LEVEL_FIELDS) {
|
|
240
|
+
const value = normalizeText(config[field]);
|
|
241
|
+
if (value) return value;
|
|
242
|
+
}
|
|
243
|
+
return "";
|
|
244
|
+
}
|
|
245
|
+
|
|
231
246
|
function resolveBossChatScreenConfig(workspaceRoot) {
|
|
232
247
|
const resolution = getScreenConfigResolution(workspaceRoot);
|
|
233
248
|
const configPath = resolution.resolved_path || resolution.writable_path || resolution.legacy_path || null;
|
|
@@ -274,6 +289,7 @@ function resolveBossChatScreenConfig(workspaceRoot) {
|
|
|
274
289
|
baseUrl: normalizeText(parsed.baseUrl).replace(/\/+$/, ""),
|
|
275
290
|
apiKey: normalizeText(parsed.apiKey),
|
|
276
291
|
model: normalizeText(parsed.model),
|
|
292
|
+
llmThinkingLevel: resolveLlmThinkingLevel(parsed),
|
|
277
293
|
debugPort: parsePositiveInteger(parsed.debugPort, 9222)
|
|
278
294
|
},
|
|
279
295
|
config_path: configPath,
|
|
@@ -385,6 +401,9 @@ function buildBossChatCliArgs(command, input, resolvedConfig) {
|
|
|
385
401
|
args.push("--baseurl", resolvedConfig.baseUrl);
|
|
386
402
|
args.push("--apikey", resolvedConfig.apiKey);
|
|
387
403
|
args.push("--model", resolvedConfig.model);
|
|
404
|
+
if (resolvedConfig.llmThinkingLevel) {
|
|
405
|
+
args.push("--thinking-level", resolvedConfig.llmThinkingLevel);
|
|
406
|
+
}
|
|
388
407
|
return args;
|
|
389
408
|
}
|
|
390
409
|
|
|
@@ -402,6 +421,9 @@ function buildBossChatCliArgs(command, input, resolvedConfig) {
|
|
|
402
421
|
args.push("--baseurl", resolvedConfig.baseUrl);
|
|
403
422
|
args.push("--apikey", resolvedConfig.apiKey);
|
|
404
423
|
args.push("--model", resolvedConfig.model);
|
|
424
|
+
if (resolvedConfig.llmThinkingLevel) {
|
|
425
|
+
args.push("--thinking-level", resolvedConfig.llmThinkingLevel);
|
|
426
|
+
}
|
|
405
427
|
args.push("--port", String(normalized.port || resolvedConfig.debugPort || 9222));
|
|
406
428
|
if (typeof normalized.safePacing === "boolean") {
|
|
407
429
|
args.push("--safe-pacing", String(normalized.safePacing));
|
package/src/cli.js
CHANGED
|
@@ -768,6 +768,11 @@ function setScreeningConfig(options = {}) {
|
|
|
768
768
|
apiKey,
|
|
769
769
|
model
|
|
770
770
|
};
|
|
771
|
+
if (typeof options["thinking-level"] === "string" && options["thinking-level"].trim()) {
|
|
772
|
+
nextConfig.llmThinkingLevel = options["thinking-level"].trim();
|
|
773
|
+
} else if (typeof options.llmThinkingLevel === "string" && options.llmThinkingLevel.trim()) {
|
|
774
|
+
nextConfig.llmThinkingLevel = options.llmThinkingLevel.trim();
|
|
775
|
+
}
|
|
771
776
|
if (typeof options["openai-organization"] === "string") {
|
|
772
777
|
nextConfig.openaiOrganization = options["openai-organization"];
|
|
773
778
|
}
|
|
@@ -1247,7 +1252,7 @@ function printHelp() {
|
|
|
1247
1252
|
console.log("Run command:");
|
|
1248
1253
|
console.log(" boss-recommend-mcp run --instruction \"推荐页上筛选211男生,近14天没有,有大模型平台经验\" [--confirmation-json '{...}'] [--overrides-json '{...}'] [--follow-up-json '{...}']");
|
|
1249
1254
|
console.log(" boss-recommend-mcp chat run --job \"算法工程师\" --start-from unread --criteria \"有 AI Agent 经验\" --targetCount 20 # 后台启动,不自动轮询");
|
|
1250
|
-
console.log(" boss-recommend-mcp config set --base-url <url> --api-key <key> --model <model> [--openai-organization <id>] [--openai-project <id>]");
|
|
1255
|
+
console.log(" boss-recommend-mcp config set --base-url <url> --api-key <key> --model <model> [--thinking-level off|low|medium|high|current] [--openai-organization <id>] [--openai-project <id>]");
|
|
1251
1256
|
console.log(" boss-recommend-mcp install --agent trae-cn");
|
|
1252
1257
|
console.log(" boss-recommend-mcp doctor --agent trae-cn --page-scope featured");
|
|
1253
1258
|
console.log(" boss-recommend-mcp calibrate --port 9222 [--timeout-ms 60000] [--output <path>]");
|
package/src/test-boss-chat.js
CHANGED
|
@@ -717,6 +717,94 @@ async function testBossChatLlmTextChunkFallbackShouldWork() {
|
|
|
717
717
|
}
|
|
718
718
|
}
|
|
719
719
|
|
|
720
|
+
async function testBossChatLlmShouldApplyThinkingDefaultsAndOverrides() {
|
|
721
|
+
const completionResponse = {
|
|
722
|
+
ok: true,
|
|
723
|
+
status: 200,
|
|
724
|
+
async json() {
|
|
725
|
+
return {
|
|
726
|
+
choices: [
|
|
727
|
+
{
|
|
728
|
+
message: {
|
|
729
|
+
content: "{\"passed\": false, \"reason\": \"not matched\", \"summary\": \"not matched\", \"evidence\": [\"resume\"]}"
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
]
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
};
|
|
736
|
+
const responsesResponse = {
|
|
737
|
+
ok: true,
|
|
738
|
+
status: 200,
|
|
739
|
+
async json() {
|
|
740
|
+
return {
|
|
741
|
+
output_text: "{\"passed\": false, \"reason\": \"not matched\", \"summary\": \"not matched\", \"evidence\": [\"resume\"]}"
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
};
|
|
745
|
+
|
|
746
|
+
let volcCompletionPayload = null;
|
|
747
|
+
const volcClient = new LlmClient({
|
|
748
|
+
baseUrl: "https://ark.cn-beijing.volces.com/api/v3",
|
|
749
|
+
apiKey: "sk-test",
|
|
750
|
+
model: "doubao-seed-2-0-mini-260215",
|
|
751
|
+
}, {
|
|
752
|
+
fetchImpl: async (_url, options = {}) => {
|
|
753
|
+
volcCompletionPayload = JSON.parse(String(options.body || "{}"));
|
|
754
|
+
return completionResponse;
|
|
755
|
+
},
|
|
756
|
+
});
|
|
757
|
+
await volcClient.requestCompletions({ prompt: "prompt", evidenceCorpus: "resume" });
|
|
758
|
+
assert.deepEqual(volcCompletionPayload.thinking, { type: "disabled" });
|
|
759
|
+
assert.equal(volcCompletionPayload.reasoning_effort, "minimal");
|
|
760
|
+
|
|
761
|
+
let lowCompletionPayload = null;
|
|
762
|
+
const lowClient = new LlmClient({
|
|
763
|
+
baseUrl: "https://ark.cn-beijing.volces.com/api/v3",
|
|
764
|
+
apiKey: "sk-test",
|
|
765
|
+
model: "doubao-seed-2-0-mini-260215",
|
|
766
|
+
thinkingLevel: "low",
|
|
767
|
+
}, {
|
|
768
|
+
fetchImpl: async (_url, options = {}) => {
|
|
769
|
+
lowCompletionPayload = JSON.parse(String(options.body || "{}"));
|
|
770
|
+
return completionResponse;
|
|
771
|
+
},
|
|
772
|
+
});
|
|
773
|
+
await lowClient.requestCompletions({ prompt: "prompt", evidenceCorpus: "resume" });
|
|
774
|
+
assert.deepEqual(lowCompletionPayload.thinking, { type: "enabled" });
|
|
775
|
+
assert.equal(lowCompletionPayload.reasoning_effort, "low");
|
|
776
|
+
|
|
777
|
+
let openaiCompletionPayload = null;
|
|
778
|
+
const openaiClient = new LlmClient({
|
|
779
|
+
baseUrl: "https://api.openai.com/v1",
|
|
780
|
+
apiKey: "sk-test",
|
|
781
|
+
model: "gpt-test",
|
|
782
|
+
}, {
|
|
783
|
+
fetchImpl: async (_url, options = {}) => {
|
|
784
|
+
openaiCompletionPayload = JSON.parse(String(options.body || "{}"));
|
|
785
|
+
return completionResponse;
|
|
786
|
+
},
|
|
787
|
+
});
|
|
788
|
+
await openaiClient.requestCompletions({ prompt: "prompt", evidenceCorpus: "resume" });
|
|
789
|
+
assert.equal(openaiCompletionPayload.thinking, undefined);
|
|
790
|
+
assert.equal(openaiCompletionPayload.reasoning_effort, "minimal");
|
|
791
|
+
|
|
792
|
+
let responsesPayload = null;
|
|
793
|
+
const responsesClient = new LlmClient({
|
|
794
|
+
baseUrl: "https://api.openai.com/v1",
|
|
795
|
+
apiKey: "sk-test",
|
|
796
|
+
model: "gpt-test",
|
|
797
|
+
thinkingLevel: "low",
|
|
798
|
+
}, {
|
|
799
|
+
fetchImpl: async (_url, options = {}) => {
|
|
800
|
+
responsesPayload = JSON.parse(String(options.body || "{}"));
|
|
801
|
+
return responsesResponse;
|
|
802
|
+
},
|
|
803
|
+
});
|
|
804
|
+
await responsesClient.requestResponses({ prompt: "prompt", evidenceCorpus: "resume" });
|
|
805
|
+
assert.deepEqual(responsesPayload.reasoning, { effort: "low" });
|
|
806
|
+
}
|
|
807
|
+
|
|
720
808
|
async function testBossChatAppShouldPersistEvidenceArtifacts() {
|
|
721
809
|
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "boss-chat-artifacts-"));
|
|
722
810
|
await mkdir(tempDir, { recursive: true });
|
|
@@ -846,6 +934,7 @@ async function main() {
|
|
|
846
934
|
testBossChatLlmEvidenceGateShouldDemoteMissingEvidence();
|
|
847
935
|
testBossChatLlmEvidenceGateShouldDemoteUnmatchedEvidence();
|
|
848
936
|
await testBossChatLlmTextChunkFallbackShouldWork();
|
|
937
|
+
await testBossChatLlmShouldApplyThinkingDefaultsAndOverrides();
|
|
849
938
|
await testBossChatAppShouldPersistEvidenceArtifacts();
|
|
850
939
|
console.log("boss-chat tests passed");
|
|
851
940
|
}
|
|
@@ -249,6 +249,12 @@ function parseArgs(argv) {
|
|
|
249
249
|
case 'model':
|
|
250
250
|
args.overrides.llm.model = value || '';
|
|
251
251
|
break;
|
|
252
|
+
case 'thinking-level':
|
|
253
|
+
case 'thinkingLevel':
|
|
254
|
+
case 'llm-thinking-level':
|
|
255
|
+
case 'reasoning-effort':
|
|
256
|
+
args.overrides.llm.thinkingLevel = value || '';
|
|
257
|
+
break;
|
|
252
258
|
case 'port':
|
|
253
259
|
args.overrides.chrome.port = Number.parseInt(value, 10);
|
|
254
260
|
break;
|
|
@@ -299,6 +305,7 @@ function printUsage() {
|
|
|
299
305
|
console.log(' --baseurl <url> Override LLM base URL');
|
|
300
306
|
console.log(' --apikey <key> Override LLM API key');
|
|
301
307
|
console.log(' --model <name> Override LLM model');
|
|
308
|
+
console.log(' --thinking-level <level> LLM thinking level: off|low|medium|high|current');
|
|
302
309
|
console.log(' --port <n> Override Chrome remote debugging port');
|
|
303
310
|
}
|
|
304
311
|
|
|
@@ -768,6 +775,9 @@ function buildDetachedRunArgs(args, runId) {
|
|
|
768
775
|
if (args.overrides.llm.model) {
|
|
769
776
|
workerArgs.push('--model', String(args.overrides.llm.model));
|
|
770
777
|
}
|
|
778
|
+
if (args.overrides.llm.thinkingLevel) {
|
|
779
|
+
workerArgs.push('--thinking-level', String(args.overrides.llm.thinkingLevel));
|
|
780
|
+
}
|
|
771
781
|
if (Number.isFinite(args.overrides.chrome.port)) {
|
|
772
782
|
workerArgs.push('--port', String(args.overrides.chrome.port));
|
|
773
783
|
}
|
|
@@ -74,6 +74,7 @@ function registerTools(server) {
|
|
|
74
74
|
baseUrl: z.string().optional().describe('覆盖 LLM baseUrl'),
|
|
75
75
|
apiKey: z.string().optional().describe('覆盖 LLM apiKey'),
|
|
76
76
|
model: z.string().optional().describe('覆盖 LLM 模型'),
|
|
77
|
+
thinkingLevel: z.string().optional().describe('覆盖 LLM thinking/reasoning 级别:off/low/medium/high/current'),
|
|
77
78
|
port: z.number().int().positive().optional().describe('Chrome 调试端口'),
|
|
78
79
|
safePacing: z.boolean().optional().describe('是否启用安全节奏控制'),
|
|
79
80
|
batchRestEnabled: z.boolean().optional().describe('是否启用批次休息'),
|
|
@@ -146,4 +147,3 @@ main().catch((error) => {
|
|
|
146
147
|
console.error('[boss-chat-mcp] server failed:', error?.stack || error?.message || String(error));
|
|
147
148
|
process.exit(1);
|
|
148
149
|
});
|
|
149
|
-
|
|
@@ -71,6 +71,7 @@ export function buildCliArgs(command, input = {}) {
|
|
|
71
71
|
pushValueArg(args, 'baseurl', input.baseUrl);
|
|
72
72
|
pushValueArg(args, 'apikey', input.apiKey);
|
|
73
73
|
pushValueArg(args, 'model', input.model);
|
|
74
|
+
pushValueArg(args, 'thinking-level', input.thinkingLevel || input.llmThinkingLevel || input.reasoningEffort);
|
|
74
75
|
|
|
75
76
|
const port = normalizePositiveInt(input.port);
|
|
76
77
|
if (port) {
|
|
@@ -190,4 +191,3 @@ export async function runCliJsonCommand(command, input = {}) {
|
|
|
190
191
|
});
|
|
191
192
|
});
|
|
192
193
|
}
|
|
193
|
-
|
|
@@ -4,6 +4,12 @@ const DEFAULT_TEXT_MODEL_CHUNK_SIZE_CHARS = 24000;
|
|
|
4
4
|
const DEFAULT_TEXT_MODEL_CHUNK_OVERLAP_CHARS = 1200;
|
|
5
5
|
const DEFAULT_TEXT_MODEL_MAX_CHUNKS = 12;
|
|
6
6
|
const MAX_EVIDENCE_TOKENS = 12;
|
|
7
|
+
const LLM_THINKING_ENV_KEYS = [
|
|
8
|
+
'BOSS_CHAT_LLM_THINKING_LEVEL',
|
|
9
|
+
'BOSS_RECOMMEND_LLM_THINKING_LEVEL',
|
|
10
|
+
'BOSS_LLM_THINKING_LEVEL',
|
|
11
|
+
'LLM_THINKING_LEVEL',
|
|
12
|
+
];
|
|
7
13
|
|
|
8
14
|
function normalizeText(value) {
|
|
9
15
|
return String(value || '').replace(/\s+/g, ' ').trim();
|
|
@@ -67,6 +73,84 @@ function normalizeBool(value, fallback = false) {
|
|
|
67
73
|
return fallback;
|
|
68
74
|
}
|
|
69
75
|
|
|
76
|
+
function normalizeLlmThinkingLevel(value) {
|
|
77
|
+
const normalized = normalizeText(value).toLowerCase().replace(/[_\s]+/g, '-');
|
|
78
|
+
if (!normalized) return '';
|
|
79
|
+
if (['off', 'disabled', 'disable', 'minimal', 'none', 'false', '0'].includes(normalized)) return 'off';
|
|
80
|
+
if (
|
|
81
|
+
['low', 'medium', 'high', 'auto', 'current', 'default', 'provider-default', 'unchanged', 'inherit'].includes(
|
|
82
|
+
normalized,
|
|
83
|
+
)
|
|
84
|
+
) {
|
|
85
|
+
return normalized;
|
|
86
|
+
}
|
|
87
|
+
return '';
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function getEnvLlmThinkingLevel() {
|
|
91
|
+
for (const key of LLM_THINKING_ENV_KEYS) {
|
|
92
|
+
const normalized = normalizeLlmThinkingLevel(process.env[key]);
|
|
93
|
+
if (normalized) return normalized;
|
|
94
|
+
}
|
|
95
|
+
return '';
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function resolveLlmThinkingLevel(config = {}, options = {}) {
|
|
99
|
+
return (
|
|
100
|
+
normalizeLlmThinkingLevel(options.thinkingLevel) ||
|
|
101
|
+
normalizeLlmThinkingLevel(options.llmThinkingLevel) ||
|
|
102
|
+
normalizeLlmThinkingLevel(config.llmThinkingLevel) ||
|
|
103
|
+
normalizeLlmThinkingLevel(config.thinkingLevel) ||
|
|
104
|
+
normalizeLlmThinkingLevel(config.reasoningEffort) ||
|
|
105
|
+
normalizeLlmThinkingLevel(config.reasoning_effort) ||
|
|
106
|
+
getEnvLlmThinkingLevel() ||
|
|
107
|
+
'off'
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function isProviderDefaultThinkingLevel(level) {
|
|
112
|
+
return ['current', 'default', 'provider-default', 'unchanged', 'inherit'].includes(level);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function isVolcengineModel(baseUrl, model) {
|
|
116
|
+
const combined = `${baseUrl || ''} ${model || ''}`;
|
|
117
|
+
return /volces\.com|volcengine|ark\.cn-|doubao|seed/i.test(combined);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function applyChatCompletionThinking(payload, { baseUrl = '', model = '', thinkingLevel = '' } = {}) {
|
|
121
|
+
const level = normalizeLlmThinkingLevel(thinkingLevel) || 'off';
|
|
122
|
+
if (isProviderDefaultThinkingLevel(level)) return payload;
|
|
123
|
+
const isVolc = isVolcengineModel(baseUrl, model);
|
|
124
|
+
if (isVolc) {
|
|
125
|
+
if (level === 'auto') {
|
|
126
|
+
payload.thinking = { type: 'auto' };
|
|
127
|
+
return payload;
|
|
128
|
+
}
|
|
129
|
+
if (level === 'off') {
|
|
130
|
+
payload.thinking = { type: 'disabled' };
|
|
131
|
+
payload.reasoning_effort = 'minimal';
|
|
132
|
+
return payload;
|
|
133
|
+
}
|
|
134
|
+
payload.thinking = { type: 'enabled' };
|
|
135
|
+
payload.reasoning_effort = level;
|
|
136
|
+
return payload;
|
|
137
|
+
}
|
|
138
|
+
if (level !== 'auto') {
|
|
139
|
+
payload.reasoning_effort = level === 'off' ? 'minimal' : level;
|
|
140
|
+
}
|
|
141
|
+
return payload;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function applyResponsesThinking(payload, { thinkingLevel = '' } = {}) {
|
|
145
|
+
const level = normalizeLlmThinkingLevel(thinkingLevel) || 'off';
|
|
146
|
+
if (isProviderDefaultThinkingLevel(level) || level === 'auto') return payload;
|
|
147
|
+
payload.reasoning = {
|
|
148
|
+
...(payload.reasoning || {}),
|
|
149
|
+
effort: level === 'off' ? 'minimal' : level,
|
|
150
|
+
};
|
|
151
|
+
return payload;
|
|
152
|
+
}
|
|
153
|
+
|
|
70
154
|
function toStringArray(value, maxItems = 8) {
|
|
71
155
|
if (!Array.isArray(value)) return [];
|
|
72
156
|
const normalized = [];
|
|
@@ -378,8 +462,9 @@ export class LlmClient {
|
|
|
378
462
|
options.preferCompletions !== undefined
|
|
379
463
|
? normalizeBool(options.preferCompletions, false)
|
|
380
464
|
: config.preferCompletions !== undefined
|
|
381
|
-
|
|
382
|
-
|
|
465
|
+
? normalizeBool(config.preferCompletions, false)
|
|
466
|
+
: /doubao|seed/i.test(String(this.model || ''));
|
|
467
|
+
this.thinkingLevel = resolveLlmThinkingLevel(config, options);
|
|
383
468
|
}
|
|
384
469
|
|
|
385
470
|
async readImageAsDataUrl(imagePath) {
|
|
@@ -405,23 +490,26 @@ export class LlmClient {
|
|
|
405
490
|
if (imageDataUrl) {
|
|
406
491
|
content.push({ type: 'input_image', image_url: imageDataUrl });
|
|
407
492
|
}
|
|
493
|
+
const payload = {
|
|
494
|
+
model: this.model,
|
|
495
|
+
temperature: 0.1,
|
|
496
|
+
max_output_tokens: this.responseMaxOutputTokens,
|
|
497
|
+
input: [
|
|
498
|
+
{
|
|
499
|
+
role: 'user',
|
|
500
|
+
content,
|
|
501
|
+
},
|
|
502
|
+
],
|
|
503
|
+
};
|
|
504
|
+
applyResponsesThinking(payload, { thinkingLevel: this.thinkingLevel });
|
|
505
|
+
|
|
408
506
|
const response = await this.fetchImpl(`${this.baseUrl}/responses`, {
|
|
409
507
|
method: 'POST',
|
|
410
508
|
headers: {
|
|
411
509
|
'Content-Type': 'application/json',
|
|
412
510
|
Authorization: `Bearer ${this.apiKey}`,
|
|
413
511
|
},
|
|
414
|
-
body: JSON.stringify(
|
|
415
|
-
model: this.model,
|
|
416
|
-
temperature: 0.1,
|
|
417
|
-
max_output_tokens: this.responseMaxOutputTokens,
|
|
418
|
-
input: [
|
|
419
|
-
{
|
|
420
|
-
role: 'user',
|
|
421
|
-
content,
|
|
422
|
-
},
|
|
423
|
-
],
|
|
424
|
-
}),
|
|
512
|
+
body: JSON.stringify(payload),
|
|
425
513
|
signal: AbortSignal.timeout(this.timeoutMs),
|
|
426
514
|
});
|
|
427
515
|
|
|
@@ -477,23 +565,30 @@ export class LlmClient {
|
|
|
477
565
|
if (imageDataUrl) {
|
|
478
566
|
content.push({ type: 'image_url', image_url: { url: imageDataUrl } });
|
|
479
567
|
}
|
|
568
|
+
const payload = {
|
|
569
|
+
model: this.model,
|
|
570
|
+
temperature: 0.1,
|
|
571
|
+
max_tokens: this.completionMaxTokens,
|
|
572
|
+
messages: [
|
|
573
|
+
{
|
|
574
|
+
role: 'user',
|
|
575
|
+
content,
|
|
576
|
+
},
|
|
577
|
+
],
|
|
578
|
+
};
|
|
579
|
+
applyChatCompletionThinking(payload, {
|
|
580
|
+
baseUrl: this.baseUrl,
|
|
581
|
+
model: this.model,
|
|
582
|
+
thinkingLevel: this.thinkingLevel,
|
|
583
|
+
});
|
|
584
|
+
|
|
480
585
|
const response = await this.fetchImpl(`${this.baseUrl}/chat/completions`, {
|
|
481
586
|
method: 'POST',
|
|
482
587
|
headers: {
|
|
483
588
|
'Content-Type': 'application/json',
|
|
484
589
|
Authorization: `Bearer ${this.apiKey}`,
|
|
485
590
|
},
|
|
486
|
-
body: JSON.stringify(
|
|
487
|
-
model: this.model,
|
|
488
|
-
temperature: 0.1,
|
|
489
|
-
max_tokens: this.completionMaxTokens,
|
|
490
|
-
messages: [
|
|
491
|
-
{
|
|
492
|
-
role: 'user',
|
|
493
|
-
content,
|
|
494
|
-
},
|
|
495
|
-
],
|
|
496
|
-
}),
|
|
591
|
+
body: JSON.stringify(payload),
|
|
497
592
|
signal: AbortSignal.timeout(this.timeoutMs),
|
|
498
593
|
});
|
|
499
594
|
|
|
@@ -10,6 +10,7 @@ const DEFAULT_PROFILE = {
|
|
|
10
10
|
baseUrl: '',
|
|
11
11
|
apiKey: '',
|
|
12
12
|
model: '',
|
|
13
|
+
thinkingLevel: '',
|
|
13
14
|
},
|
|
14
15
|
chrome: {
|
|
15
16
|
port: 9222,
|
|
@@ -78,6 +79,7 @@ export function toPersistentProfile(profile = {}) {
|
|
|
78
79
|
baseUrl: normalized.llm.baseUrl,
|
|
79
80
|
apiKey: normalized.llm.apiKey,
|
|
80
81
|
model: normalized.llm.model,
|
|
82
|
+
thinkingLevel: normalized.llm.thinkingLevel,
|
|
81
83
|
},
|
|
82
84
|
chrome: {
|
|
83
85
|
port: normalized.chrome.port,
|
|
@@ -99,6 +101,9 @@ export function normalizeProfile(profile = {}) {
|
|
|
99
101
|
merged.llm.baseUrl = String(merged.llm.baseUrl || '').trim().replace(/\/+$/, '');
|
|
100
102
|
merged.llm.apiKey = String(merged.llm.apiKey || '').trim();
|
|
101
103
|
merged.llm.model = String(merged.llm.model || '').trim();
|
|
104
|
+
merged.llm.thinkingLevel = String(
|
|
105
|
+
merged.llm.thinkingLevel || merged.llm.llmThinkingLevel || merged.llm.reasoningEffort || merged.llm.reasoning_effort || '',
|
|
106
|
+
).trim();
|
|
102
107
|
merged.runtime.batchRestEnabled = merged.runtime.batchRestEnabled !== false;
|
|
103
108
|
merged.runtime.safePacing = merged.runtime.safePacing !== false;
|
|
104
109
|
return merged;
|