mslxdff 0.1.105 → 0.1.106
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/auto.js +3 -3
- package/src/chat/engine.js +2 -2
- package/src/chat/gateway.js +6 -2
- package/src/chat/orchestrator.js +7 -0
- package/src/chat/prompt.js +2 -2
- package/src/chat/repl.js +11 -3
- package/src/chat/terminal.js +40 -0
- package/src/chat-pipeline/index.js +14 -3
- package/src/chat-pipeline/policy.js +3 -0
- package/src/cli/commands/provider/bench-via.js +0 -9
- package/src/cli/commands/provider/bench.js +0 -8
- package/src/cli/commands/provider/index.js +0 -4
- package/src/cli/commands/provider/models.js +0 -3
- package/src/providers/registry.js +0 -5
- package/src/routes/hedge.js +1 -2
- package/src/runtime/providers-setup.js +1 -2
- package/src/cli/commands/provider/deepseek-health.js +0 -46
- package/src/cli/commands/provider/deepseek-login.js +0 -89
- package/src/providers/deepseek/auth.js +0 -194
- package/src/providers/deepseek/bridge.js +0 -158
- package/src/providers/deepseek/chat.js +0 -233
- package/src/providers/deepseek/debug.js +0 -44
- package/src/providers/deepseek/hash-reference.js +0 -117
- package/src/providers/deepseek/hash.js +0 -245
- package/src/providers/deepseek/health.js +0 -104
- package/src/providers/deepseek/index.js +0 -180
- package/src/providers/deepseek/pow.js +0 -99
- package/src/providers/deepseek/session.js +0 -117
- package/src/providers/deepseek/sse-decoder.js +0 -160
- package/src/providers/deepseek.js +0 -1
package/package.json
CHANGED
package/src/auto.js
CHANGED
|
@@ -29,10 +29,10 @@ export const DEFAULT_AUTO_MODELS = [
|
|
|
29
29
|
PREFERRED_MODEL,
|
|
30
30
|
"mimo-v2.5-free",
|
|
31
31
|
"deepseek-v4-flash-free",
|
|
32
|
-
"ling-3.0-flash-free",
|
|
32
|
+
"ling-3.0-flash-fin-free",
|
|
33
33
|
"nemotron-3-ultra-free",
|
|
34
|
-
"
|
|
35
|
-
"
|
|
34
|
+
"nemotron-3.5-lightning-free",
|
|
35
|
+
"muse-spark-1.3-contributor-free",
|
|
36
36
|
].filter((id, i, arr) => id && arr.indexOf(id) === i);
|
|
37
37
|
|
|
38
38
|
export function isAutoModel(model) {
|
package/src/chat/engine.js
CHANGED
|
@@ -43,7 +43,7 @@ export function createEngine({
|
|
|
43
43
|
return next;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
async function runTurn(userText, messages) {
|
|
46
|
+
async function runTurn(userText, messages, modelOverride) {
|
|
47
47
|
const tools = GTOOLS();
|
|
48
48
|
messages.push({ role: "user", content: userText });
|
|
49
49
|
let loops = 0;
|
|
@@ -70,7 +70,7 @@ export function createEngine({
|
|
|
70
70
|
const tCall = performance.now();
|
|
71
71
|
let res;
|
|
72
72
|
const activeTools = forceNoTools ? [] : tools;
|
|
73
|
-
res = await CWF({ messages, tools: activeTools });
|
|
73
|
+
res = await CWF({ messages, tools: activeTools, model: modelOverride || undefined });
|
|
74
74
|
if (forceNoTools && res.ok && res.message?.tool_calls?.length) {
|
|
75
75
|
onTrace(`[guard] 禁工具模式下仍收到 tool_calls,已拦截`);
|
|
76
76
|
res.message.tool_calls = [];
|
package/src/chat/gateway.js
CHANGED
|
@@ -41,7 +41,7 @@ export function createGatewayClient({
|
|
|
41
41
|
return { data: [] };
|
|
42
42
|
});
|
|
43
43
|
|
|
44
|
-
async function chatViaGateway({ messages, tools }) {
|
|
44
|
+
async function chatViaGateway({ messages, tools, autoProvider = "opencode" }) {
|
|
45
45
|
const TRACE = env.MSLXDFF_CHAT_TRACE !== "0";
|
|
46
46
|
const t0 = TRACE ? performance.now() : 0;
|
|
47
47
|
let port = defaultPort;
|
|
@@ -72,7 +72,11 @@ export function createGatewayClient({
|
|
|
72
72
|
const timer = setTimeout(() => controller.abort(), gatewayTimeoutMs);
|
|
73
73
|
const res = await _fetch(url, {
|
|
74
74
|
method: "POST",
|
|
75
|
-
headers: {
|
|
75
|
+
headers: {
|
|
76
|
+
"Content-Type": "application/json",
|
|
77
|
+
...(autoProvider ? { "x-mslxdff-auto-provider": autoProvider } : {}),
|
|
78
|
+
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
|
79
|
+
},
|
|
76
80
|
body: JSON.stringify(body),
|
|
77
81
|
signal: controller.signal,
|
|
78
82
|
});
|
package/src/chat/orchestrator.js
CHANGED
|
@@ -53,6 +53,13 @@ export function createOrchestrator({
|
|
|
53
53
|
|
|
54
54
|
async function chatWithFallback(opts) {
|
|
55
55
|
if (chatWithFallbackImpl) return chatWithFallbackImpl(opts);
|
|
56
|
+
// 严格模式:显式指定了具体模型(非 auto/空)→ 只用该模型,失败即报错,绝不降级到其他模型
|
|
57
|
+
const pinned = String(opts?.model || "").trim();
|
|
58
|
+
if (pinned && pinned.toLowerCase() !== "auto") {
|
|
59
|
+
const r = await safeChatOnce(opts, pinned);
|
|
60
|
+
if (r.ok) return { ...r, model: pinned };
|
|
61
|
+
return { ok: false, error: `指定模型 ${pinned} 失败:${r.error}(已锁定不自动换模型;如需自动择优请输入 /model auto)`, status: r.status || 502, pinnedModel: pinned };
|
|
62
|
+
}
|
|
56
63
|
const TRACE = env.MSLXDFF_CHAT_TRACE !== "0";
|
|
57
64
|
const HEDGE_MS = (() => {
|
|
58
65
|
const v = Number(env.MSLXDFF_HEDGE_DELAY_MS);
|
package/src/chat/prompt.js
CHANGED
|
@@ -22,8 +22,8 @@ function readModels() {
|
|
|
22
22
|
if (ids.length) return ids;
|
|
23
23
|
}
|
|
24
24
|
} catch {}
|
|
25
|
-
// 兜底:硬编码常见 free
|
|
26
|
-
return ["mimo-v2.5-free", "
|
|
25
|
+
// 兜底:硬编码常见 free 模型,供离线时参考(2026-09 实测 /zen/v1/models free 池)
|
|
26
|
+
return ["big-pickle", "mimo-v2.5-free", "ling-3.0-flash-fin-free", "deepseek-v4-flash-free", "nemotron-3.5-lightning-free", "nemotron-3-ultra-free", "muse-spark-1.3-contributor-free", "muse-spark-1.2-contributor-free"];
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
export function buildSystemPrompt({ modelsOverride } = {}) {
|
package/src/chat/repl.js
CHANGED
|
@@ -31,6 +31,12 @@ export async function startRepl({ singleShot } = {}) {
|
|
|
31
31
|
curlTool,
|
|
32
32
|
onTrace: trace,
|
|
33
33
|
});
|
|
34
|
+
let pinnedModel = null; // /model 锁定的模型:严格单模型,不通即报错(null=默认链)
|
|
35
|
+
|
|
36
|
+
function applyPrompt() {
|
|
37
|
+
const label = pinnedModel || CHAT_PREFERRED.split("-")[0];
|
|
38
|
+
rl.setPrompt(`\x1b[36m${label}>\x1b[0m `);
|
|
39
|
+
}
|
|
34
40
|
|
|
35
41
|
if (singleShot) {
|
|
36
42
|
const text = String(singleShot).trim();
|
|
@@ -47,22 +53,24 @@ export async function startRepl({ singleShot } = {}) {
|
|
|
47
53
|
|
|
48
54
|
await printBanner();
|
|
49
55
|
const rl = createReadline(`\x1b[36m${CHAT_PREFERRED.split("-")[0]}>\x1b[0m `);
|
|
56
|
+
applyPrompt();
|
|
50
57
|
rl.prompt();
|
|
51
58
|
for await (const line of rl) {
|
|
52
59
|
const raw = String(line || "").trim();
|
|
53
60
|
if (!raw) { rl.prompt(); continue; }
|
|
54
|
-
const slash = handleSlash(raw, { messages, system });
|
|
61
|
+
const slash = handleSlash(raw, { messages, system, pinnedModel });
|
|
55
62
|
if (slash.handled) {
|
|
56
63
|
if (slash.exit) { console.log("再见"); saveHistory(messages.slice(1)); rl.close(); return; }
|
|
57
64
|
if (slash.messages) messages = slash.messages;
|
|
65
|
+
if ("pinnedModel" in slash) { pinnedModel = slash.pinnedModel; applyPrompt(); }
|
|
58
66
|
rl.prompt();
|
|
59
67
|
continue;
|
|
60
68
|
}
|
|
61
69
|
try {
|
|
62
|
-
const spinner = createSpinner("已发送给 AI,等待回复中");
|
|
70
|
+
const spinner = createSpinner(pinnedModel ? `已发送给 ${pinnedModel},等待回复中` : "已发送给 AI,等待回复中");
|
|
63
71
|
spinner.start();
|
|
64
72
|
let r;
|
|
65
|
-
try { r = await engine.runTurn(raw, messages); }
|
|
73
|
+
try { r = await engine.runTurn(raw, messages, pinnedModel); }
|
|
66
74
|
finally { spinner.stop(`\x1b[90m✓ AI 已回复\x1b[0m`); }
|
|
67
75
|
if (r.text && !r.text.startsWith("OK") && !r.text.startsWith("FAIL")) console.log(r.text);
|
|
68
76
|
if (r.model || r.latency) printFooter(r);
|
package/src/chat/terminal.js
CHANGED
|
@@ -3,10 +3,25 @@ import { stdin, stdout } from "node:process";
|
|
|
3
3
|
import { formatBannerLines, formatStatsDetail, collectStats, probeGateway } from "./stats.js";
|
|
4
4
|
import { createSpinner } from "./spinner.js";
|
|
5
5
|
import { normalizeFullId } from "../providers/model-id.js";
|
|
6
|
+
import { isFreeModel } from "../models.js";
|
|
7
|
+
import { getModelsForPrompt } from "./prompt.js";
|
|
6
8
|
import { loadHistory, saveHistory, clearHistory, histPath, estimateChars } from "./store.js";
|
|
7
9
|
|
|
10
|
+
// -chat 只用 opencode 上游的免费模型:裸 id(无供应商前缀)+ free 池
|
|
11
|
+
const FALLBACK_FREE_POOL = ["big-pickle", "mimo-v2.5-free", "ling-3.0-flash-fin-free", "deepseek-v4-flash-free", "nemotron-3.5-lightning-free", "nemotron-3-ultra-free", "muse-spark-1.3-contributor-free", "muse-spark-1.2-contributor-free"];
|
|
12
|
+
|
|
13
|
+
function freePoolNow() {
|
|
14
|
+
try {
|
|
15
|
+
const ids = getModelsForPrompt().filter((id) => typeof id === "string" && !id.includes("/") && isFreeModel(id));
|
|
16
|
+
if (ids.length) return ids;
|
|
17
|
+
} catch {}
|
|
18
|
+
return FALLBACK_FREE_POOL;
|
|
19
|
+
}
|
|
20
|
+
|
|
8
21
|
export const SLASH_HELP = `自然语言直接说,斜杠快捷:
|
|
9
22
|
/help 本帮助
|
|
23
|
+
/model 查看/锁定模型(仅限 opencode free 池;锁定后严格只用该模型,不通即报错)
|
|
24
|
+
/model mimo-v2.5-free 锁定 · /model auto 解除锁定(回默认 mimo→pickle→auto 链)
|
|
10
25
|
/stats 详细统计(网关 -d 的请求/延迟/模型)
|
|
11
26
|
/history 查看对话历史
|
|
12
27
|
/clear 清空历史
|
|
@@ -67,6 +82,31 @@ export function handleSlash(line, ctx) {
|
|
|
67
82
|
console.log(SLASH_HELP);
|
|
68
83
|
return { handled: true };
|
|
69
84
|
}
|
|
85
|
+
if (low.startsWith("/model")) {
|
|
86
|
+
const arg = raw.slice(6).trim();
|
|
87
|
+
if (!arg) {
|
|
88
|
+
if (ctx.pinnedModel) console.log(`\x1b[36m当前锁定模型:${ctx.pinnedModel}(严格单模型,不通即报错)\x1b[0m`);
|
|
89
|
+
else console.log(`\x1b[90m未锁定模型 · 默认链:mimo-v2.5-free → big-pickle → 网关 auto\x1b[0m`);
|
|
90
|
+
return { handled: true };
|
|
91
|
+
}
|
|
92
|
+
if (arg === "auto" || arg === "clear" || arg === "off" || arg === "解锁" || arg === "解除") {
|
|
93
|
+
console.log(`\x1b[90m已解除模型锁定 · 回默认链:mimo-v2.5-free → big-pickle → 网关 auto\x1b[0m`);
|
|
94
|
+
return { handled: true, pinnedModel: null };
|
|
95
|
+
}
|
|
96
|
+
const id = arg;
|
|
97
|
+
if (id.includes("/")) {
|
|
98
|
+
console.log(`\x1b[31m[拒绝] ${id} 带供应商前缀 — -chat 只支持 opencode 上游模型(裸 id,直连 opencode.ai 免费池)\x1b[0m`);
|
|
99
|
+
console.log(`\x1b[90m其他供应商(deepseek/ workbuddy/ clinebot/ 等)请走网关:mslxdff -model set <id> 或 curl 本机 /v1/chat/completions\x1b[0m`);
|
|
100
|
+
return { handled: true };
|
|
101
|
+
}
|
|
102
|
+
if (!isFreeModel(id) || !freePoolNow().includes(id)) {
|
|
103
|
+
console.log(`\x1b[31m[拒绝] ${id} 不在 opencode free 池 — -chat 只能用 opencode 免费模型\x1b[0m`);
|
|
104
|
+
console.log(`\x1b[90m当前 free 池:${freePoolNow().join(", ")}\x1b[0m`);
|
|
105
|
+
return { handled: true };
|
|
106
|
+
}
|
|
107
|
+
console.log(`\x1b[36m[已锁定] ${id} · 严格只用该模型,不通即报错(不自动换模型)· 解除:/model auto\x1b[0m`);
|
|
108
|
+
return { handled: true, pinnedModel: id };
|
|
109
|
+
}
|
|
70
110
|
if (["/stats", "/status", "stats", "status"].includes(low)) {
|
|
71
111
|
console.log(formatStatsDetail());
|
|
72
112
|
return { handled: true };
|
|
@@ -3,6 +3,7 @@ import { analyzePolicy } from "./policy.js";
|
|
|
3
3
|
import { planRoute } from "./planner.js";
|
|
4
4
|
import { createEngine } from "./engine.js";
|
|
5
5
|
import { runHook } from "../plugins.js";
|
|
6
|
+
import { isFreeModel } from "../models.js";
|
|
6
7
|
import { clientIp, summarizePrompt } from "../routes/helpers.js";
|
|
7
8
|
|
|
8
9
|
/**
|
|
@@ -21,7 +22,7 @@ export function createChatPipeline({ upstream, auto, logs, peers, groups, bus, t
|
|
|
21
22
|
const mark = (name) => stages.push([name, Math.round(performance.now() - perf0)]);
|
|
22
23
|
|
|
23
24
|
const policy = analyzePolicy({ headers: req?.headers || {}, body: req?.body || {} });
|
|
24
|
-
const { requested, useAuto, lockModel, hops, shareKeys, workbuddyUid, aliasInfo } = policy;
|
|
25
|
+
const { requested, useAuto, autoProvider, lockModel, hops, shareKeys, workbuddyUid, aliasInfo } = policy;
|
|
25
26
|
mark("parsed");
|
|
26
27
|
if (aliasInfo) { try { res?.setHeader?.("x-mslxdff-alias", aliasInfo); } catch {} }
|
|
27
28
|
// mslxdff/ 前缀或 alias 命中时,把 body.model 改写为还原后的模型(与原 gateway 语义一致)
|
|
@@ -31,10 +32,20 @@ export function createChatPipeline({ upstream, auto, logs, peers, groups, bus, t
|
|
|
31
32
|
|
|
32
33
|
// order 推导 + plugin model:select 可改
|
|
33
34
|
// 语义:指定模型 = 死锁单模型(本机→组员同款,挂了就报挂,不兜其他 picks);只有 auto 才轮 picks
|
|
35
|
+
// x-mslxdff-auto-provider 头可把 auto 候选限定到单供应商(opencode=裸 id 免费池),-chat 默认带
|
|
34
36
|
let order;
|
|
35
37
|
if (lockModel) order = [requested];
|
|
36
|
-
else if (useAuto)
|
|
37
|
-
|
|
38
|
+
else if (useAuto) {
|
|
39
|
+
let cands = auto ? await auto.candidates() : [""];
|
|
40
|
+
if (autoProvider) {
|
|
41
|
+
const before = cands.length;
|
|
42
|
+
cands = cands.filter((m) => (autoProvider === "opencode"
|
|
43
|
+
? !String(m).includes("/") && isFreeModel(m)
|
|
44
|
+
: String(m).startsWith(`${autoProvider}/`)));
|
|
45
|
+
evt("auto-scope", { reqId, provider: autoProvider, before, after: cands.length });
|
|
46
|
+
}
|
|
47
|
+
order = cands;
|
|
48
|
+
} else {
|
|
38
49
|
if (auto && requested) { try { await auto.candidatesFor(requested); } catch {} }
|
|
39
50
|
order = [requested];
|
|
40
51
|
}
|
|
@@ -13,6 +13,8 @@ export function analyzePolicy({ headers = {}, body = {} } = {}) {
|
|
|
13
13
|
const shareKeys = parseShareKeysHeader(headers[SHARE_KEYS_HEADER] || headers["x-mslxdff-share-keys"] || "");
|
|
14
14
|
const workbuddyUid = (headers["x-mslxdff-workbuddy-uid"] || headers["x-workbuddy-uid"] || "").toString().trim();
|
|
15
15
|
const lockModel = (headers["x-mslxdff-model-lock"] || headers["X-Mslxdff-Model-Lock"] || "").toString();
|
|
16
|
+
// auto 范围限定:x-mslxdff-auto-provider: opencode → auto 候选只留该供应商(opencode=裸 id 免费池)
|
|
17
|
+
const autoProvider = (headers["x-mslxdff-auto-provider"] || headers["X-Mslxdff-Auto-Provider"] || "").toString().trim().toLowerCase() || null;
|
|
16
18
|
const rawModel = body.model || "";
|
|
17
19
|
|
|
18
20
|
let normalizedRequested = normalizeModel(lockModel || rawModel || "");
|
|
@@ -64,6 +66,7 @@ export function analyzePolicy({ headers = {}, body = {} } = {}) {
|
|
|
64
66
|
normalizedForUpstream,
|
|
65
67
|
aliasInfo,
|
|
66
68
|
useAuto,
|
|
69
|
+
autoProvider,
|
|
67
70
|
shareKeys,
|
|
68
71
|
workbuddyUid: extractedUid,
|
|
69
72
|
lockModel,
|
|
@@ -54,13 +54,6 @@ export function filterBenchModels({ providerId, allowed, picks, allowAny = false
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
export async function handleVia({ providerId, opts, fetchImpl, loadConfigs, loadKeys, loadAllowed, loadBaseUrl, loadAllowAny, loadModelPicks, getOnlinePeersFn }) {
|
|
57
|
-
// DeepSeek 防禁言:via 也不测(显式传入直接返回;all 形态在 targetIds 收集处排除)
|
|
58
|
-
if (String(providerId || "").trim().toLowerCase() === "deepseek") {
|
|
59
|
-
const msg = "跳过 deepseek bench-via —— 网页通道易触发禁言/频率风控,体检用 mslxdff -provider deepseek health";
|
|
60
|
-
if (opts.json) console.log(JSON.stringify({ meta: { at: new Date().toISOString(), samples: opts.samples, timeout: opts.timeoutMs, includeOpencode: false, peers: [], opencodeSkipped: true, deepseekSkipped: true }, results: [], advice: msg }, null, 2));
|
|
61
|
-
else console.log(msg);
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
57
|
const { getOnlinePeers, orchestrateVia, resolveIncludeOpencode } = await import("../../../bench/via.js");
|
|
65
58
|
const peers = await (typeof getOnlinePeersFn === "function" ? getOnlinePeersFn() : getOnlinePeers());
|
|
66
59
|
if (!peers.length) {
|
|
@@ -106,8 +99,6 @@ export async function handleVia({ providerId, opts, fetchImpl, loadConfigs, load
|
|
|
106
99
|
const ids = new Set(Object.keys(configs));
|
|
107
100
|
ids.add("opencode"); ids.add("openrouter");
|
|
108
101
|
for (const pid of [...ids]) {
|
|
109
|
-
// DeepSeek 网页通道防禁言:all 形态直接排除,即使 allowlist 非空也不进候选
|
|
110
|
-
if (String(pid).toLowerCase() === "deepseek") continue;
|
|
111
102
|
const allowed = loadAllowed(pid) || [];
|
|
112
103
|
if (allowed.length) targetIds.push(pid);
|
|
113
104
|
}
|
|
@@ -36,14 +36,6 @@ export async function handleProviderBench(id, sub, rest, args, deps = {}) {
|
|
|
36
36
|
const isBench = sub === "bench" || sub === "benchmark" || sub === "eval" || sub === "test" || _isBenchViaAll;
|
|
37
37
|
if (!isBench) return false;
|
|
38
38
|
const opts = parseBenchArgs(_restArr);
|
|
39
|
-
// DeepSeek 网页通道防禁言:bench 轰炸易触发 muted/频率风控,一律跳过(体检用 health 探活代替)
|
|
40
|
-
const benchPidLower = String(id || "").trim().toLowerCase();
|
|
41
|
-
if (benchPidLower === "deepseek" || benchPidLower === "ds") {
|
|
42
|
-
const advice = "DeepSeek 网页通道(本机私有凭据)易触发禁言/频率风控,不支持 bench 测速。请用轻量体检:mslxdff -provider deepseek health";
|
|
43
|
-
if (opts.json) console.log(JSON.stringify({ ok: false, skipped: "deepseek", advice }, null, 2));
|
|
44
|
-
else console.log(`跳过 deepseek bench —— ${advice}`);
|
|
45
|
-
return true;
|
|
46
|
-
}
|
|
47
39
|
if (opts.via) {
|
|
48
40
|
const stateMod = await import("../../../state.js");
|
|
49
41
|
const loadConfigs = deps.loadProviderConfigs || stateMod.loadProviderConfigs;
|
|
@@ -65,10 +65,6 @@ export async function handleProvider(args) {
|
|
|
65
65
|
}
|
|
66
66
|
const { handleClineLogin } = await import("./cline-login.js");
|
|
67
67
|
if (await handleClineLogin(id, sub)) return true;
|
|
68
|
-
const { handleDeepseekLogin } = await import("./deepseek-login.js");
|
|
69
|
-
if (await handleDeepseekLogin(id, sub, rest)) return true;
|
|
70
|
-
const { handleDeepseekHealth } = await import("./deepseek-health.js");
|
|
71
|
-
if (await handleDeepseekHealth(id, sub, rest)) return true;
|
|
72
68
|
const { handleWorkbuddyLogin } = await import("./workbuddy-login.js");
|
|
73
69
|
if (await handleWorkbuddyLogin(id, sub, rest)) return true;
|
|
74
70
|
const { handleProviderConfig } = await import("./config.js");
|
|
@@ -28,15 +28,12 @@ export async function handleProviderModels(id, sub, args, rest) {
|
|
|
28
28
|
try {
|
|
29
29
|
const { createGenericProvider } = await import("../../../providers/generic.js");
|
|
30
30
|
const { createWorkbuddyProvider } = await import("../../../providers/workbuddy.js");
|
|
31
|
-
const { createDeepseekProvider } = await import("../../../providers/deepseek.js");
|
|
32
31
|
const baseUrl = cfg?.baseUrl || (id === "workbuddy" ? "https://copilot.tencent.com" : "");
|
|
33
32
|
const keys = loadProviderKeys(id);
|
|
34
33
|
const auths = cfg?.auths || [];
|
|
35
34
|
let provider;
|
|
36
35
|
if (id === "workbuddy") {
|
|
37
36
|
provider = createWorkbuddyProvider({ baseUrl, apiKeys: keys, auths, file: defaultStateFile() });
|
|
38
|
-
} else if (id === "deepseek") {
|
|
39
|
-
provider = createDeepseekProvider({ apiKeys: keys, file: defaultStateFile() });
|
|
40
37
|
} else {
|
|
41
38
|
if (!baseUrl) {
|
|
42
39
|
console.error(`provider ${id}: missing baseUrl — set via: mslxdff -provider ${id} set-url <baseUrl>`);
|
|
@@ -15,11 +15,6 @@ export const customProviders = [
|
|
|
15
15
|
match: (id, baseUrl) => id === "cline" || id === "clinebot" || String(baseUrl).includes("cline.bot"),
|
|
16
16
|
load: () => import("./cline.js").then((m) => m.createClineProvider),
|
|
17
17
|
},
|
|
18
|
-
{
|
|
19
|
-
id: "deepseek",
|
|
20
|
-
match: (id, baseUrl) => id === "deepseek" || String(baseUrl).includes("chat.deepseek.com"),
|
|
21
|
-
load: () => import("./deepseek.js").then((m) => m.createDeepseekProvider),
|
|
22
|
-
},
|
|
23
18
|
];
|
|
24
19
|
|
|
25
20
|
// 供 bench/probe 等需要定制化解析模型列表的场景
|
package/src/routes/hedge.js
CHANGED
|
@@ -18,8 +18,7 @@ function shouldHedge({ isStream, canForwardPeers, hedgeDelayMs: d, hasPeers, mod
|
|
|
18
18
|
if (!canForwardPeers) return false;
|
|
19
19
|
if (!hasPeers) return false;
|
|
20
20
|
if (!d || d <= 0) return false;
|
|
21
|
-
//
|
|
22
|
-
if (String(model || "").startsWith("deepseek/")) return false;
|
|
21
|
+
// 不做供应商级禁对冲特判:对冲仅按 useGroup/流式/peer 可用性决定
|
|
23
22
|
// muse-spark 走 /responses 流式(event: 包装 + 加密 reasoning),组员旧版无此整形且聚合 JSON 带错 header,必抢赢本地慢首块,需本地直出
|
|
24
23
|
if (String(model || "").toLowerCase().startsWith("muse-spark")) return false;
|
|
25
24
|
return true;
|
|
@@ -76,8 +76,7 @@ export async function setupProviders() {
|
|
|
76
76
|
const customFactory = await getCustomProviderFactory(gid, base);
|
|
77
77
|
if (customFactory) {
|
|
78
78
|
if (gid === "workbuddy" && !keys.length) continue;
|
|
79
|
-
if (gid
|
|
80
|
-
if (gid !== "workbuddy" && gid !== "deepseek" && (!base || !keys.length)) continue;
|
|
79
|
+
if (gid !== "workbuddy" && (!base || !keys.length)) continue;
|
|
81
80
|
try {
|
|
82
81
|
const provider = gid === "workbuddy"
|
|
83
82
|
? await customFactory({ baseUrl: base || "https://copilot.tencent.com", apiKeys: keys, auths })
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
// -provider deepseek health:逐账号探活(防禁言体系)
|
|
2
|
-
// 用法:mslxdff -provider deepseek health [--json]
|
|
3
|
-
// 对池内每个账号发最小真实请求(Hello world + PoW),检测 muted/限频/凭据坏;
|
|
4
|
-
// 禁言账号自动冷却 5min(解封后再次探活即自动恢复)。不经过组员、直连本机凭据。
|
|
5
|
-
export async function handleDeepseekHealth(id, sub, rest) {
|
|
6
|
-
if (id !== "deepseek" && id !== "ds") return false;
|
|
7
|
-
if (sub !== "health" && sub !== "check") return false;
|
|
8
|
-
|
|
9
|
-
const args = rest || [];
|
|
10
|
-
const jsonOut = args.some((a) => a === "--json" || a === "-json");
|
|
11
|
-
|
|
12
|
-
const { loadProviderKeys } = await import("../../../state.js");
|
|
13
|
-
const keys = loadProviderKeys("deepseek") || [];
|
|
14
|
-
if (!keys.length) {
|
|
15
|
-
console.error("❌ 没有 DeepSeek 凭据,先登录:");
|
|
16
|
-
console.error(" mslxdff -provider deepseek login --token <userToken>");
|
|
17
|
-
process.exit(1);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const { createAuthPool } = await import("../../../providers/deepseek/auth.js");
|
|
21
|
-
const { deepseekHealth } = await import("../../../providers/deepseek/health.js");
|
|
22
|
-
const authPool = createAuthPool({ tokens: keys });
|
|
23
|
-
|
|
24
|
-
if (!jsonOut) {
|
|
25
|
-
console.log(`🔍 DeepSeek 探活中(${keys.length} 个账号,逐个发最小请求)...`);
|
|
26
|
-
}
|
|
27
|
-
const report = await deepseekHealth({ authPool });
|
|
28
|
-
|
|
29
|
-
if (jsonOut) {
|
|
30
|
-
console.log(JSON.stringify(report, null, 2));
|
|
31
|
-
} else {
|
|
32
|
-
for (const r of report) {
|
|
33
|
-
console.log(` ${r.ok ? "✓" : "✗"} ...${r.tokenTail} ${r.detail}`);
|
|
34
|
-
}
|
|
35
|
-
const okCount = report.filter((r) => r.ok).length;
|
|
36
|
-
console.log(`\n结果:${okCount}/${report.length} 健康`);
|
|
37
|
-
if (okCount === 0) {
|
|
38
|
-
console.log("全部账号异常 —— 修复建议:");
|
|
39
|
-
console.log(" 1. chat.deepseek.com 登录后 F12 → Application → Local Storage → 复制 userToken");
|
|
40
|
-
console.log(" 2. mslxdff -provider deepseek login --token <userToken> 追加账号");
|
|
41
|
-
console.log(" 3. 禁言账号等待解除后再次探活即自动恢复");
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
if (report.length && report.every((r) => !r.ok)) process.exitCode = 1;
|
|
45
|
-
return true;
|
|
46
|
-
}
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
// -provider deepseek login:把 chat.deepseek.com 凭据落盘 providerConfigs.deepseek.keys
|
|
2
|
-
// 三种用法:
|
|
3
|
-
// login --token <userToken> 浏览器贴 token(F12 → Application → Local Storage → userToken)
|
|
4
|
-
// login <email|mobile> <password> 账密登录换 token(Android 协议)
|
|
5
|
-
// login 打印用法引导
|
|
6
|
-
export async function handleDeepseekLogin(id, sub, rest) {
|
|
7
|
-
if (id !== "deepseek" && id !== "ds") return false;
|
|
8
|
-
if (sub !== "login" && sub !== "auth") return false;
|
|
9
|
-
|
|
10
|
-
const args = rest || [];
|
|
11
|
-
const tokenFlagIdx = args.findIndex((a) => a === "--token" || a === "-token" || a === "token");
|
|
12
|
-
|
|
13
|
-
if (tokenFlagIdx < 0 && args.filter((a) => !String(a).startsWith("-")).length < 2) {
|
|
14
|
-
printUsage();
|
|
15
|
-
process.exit(0);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const { loadProviderKeys, saveProviderConfig, loadProviderConfig } = await import("../../../state.js");
|
|
19
|
-
|
|
20
|
-
let token = null;
|
|
21
|
-
let label = "";
|
|
22
|
-
|
|
23
|
-
if (tokenFlagIdx >= 0) {
|
|
24
|
-
token = String(args[tokenFlagIdx + 1] || "").trim();
|
|
25
|
-
if (!token) {
|
|
26
|
-
console.error("❌ --token 后面要贴 userToken 值");
|
|
27
|
-
printUsage();
|
|
28
|
-
process.exit(1);
|
|
29
|
-
}
|
|
30
|
-
label = "userToken(浏览器)";
|
|
31
|
-
} else {
|
|
32
|
-
const [loginValue, password] = args.filter((a) => !String(a).startsWith("-"));
|
|
33
|
-
console.log("🚀 DeepSeek 账密登录中(Android 协议)...");
|
|
34
|
-
try {
|
|
35
|
-
const { loginDeepseek } = await import("../../../providers/deepseek/auth.js");
|
|
36
|
-
const out = await loginDeepseek({ loginValue, password });
|
|
37
|
-
token = out.token;
|
|
38
|
-
label = `账密(${String(loginValue).slice(0, 3)}***)`;
|
|
39
|
-
console.log(`✅ 登录成功,拿到 token`);
|
|
40
|
-
} catch (e) {
|
|
41
|
-
console.error(`❌ ${e.message}`);
|
|
42
|
-
console.error(`\n换浏览器贴 token 方式:chat.deepseek.com 登录后 F12 → Application → Local Storage → 复制 userToken →`);
|
|
43
|
-
console.error(` mslxdff -provider deepseek login --token <userToken>`);
|
|
44
|
-
process.exit(1);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (!token) {
|
|
49
|
-
console.error("❌ 未拿到 token");
|
|
50
|
-
process.exit(1);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const cur = loadProviderKeys("deepseek");
|
|
54
|
-
if (cur.includes(token)) {
|
|
55
|
-
console.log(`ℹ️ 该 token 已存在(现 ${cur.length} 个账号),跳过`);
|
|
56
|
-
} else {
|
|
57
|
-
const cfg = loadProviderConfig("deepseek") || {};
|
|
58
|
-
const nextKeys = [...new Set([...(cfg.keys || cur), token].filter(Boolean))];
|
|
59
|
-
saveProviderConfig("deepseek", { ...cfg, keys: nextKeys });
|
|
60
|
-
console.log(`✅ 已写入 deepseek(现 ${nextKeys.length} 个账号)· 来源: ${label}`);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
console.log("=".repeat(60));
|
|
64
|
-
console.log("\n下一步:");
|
|
65
|
-
console.log(" mslxdff -provider deepseek allowAny on 放行模型(默认 allowlist 空=全 blocked)");
|
|
66
|
-
console.log(" mslxdff -provider deepseek models 查看支持的模型");
|
|
67
|
-
console.log(" mslxdff -restart 重启网关生效");
|
|
68
|
-
console.log("\n用法:model 字段填 deepseek/deepseek-chat 或 deepseek/deepseek-reasoner");
|
|
69
|
-
console.log(" (-search 后缀开启联网搜索:deepseek/deepseek-chat-search)");
|
|
70
|
-
console.log("\n多账号:重复 login 追加,自动轮换 + 401/429/风控切号");
|
|
71
|
-
console.log("注意:单账号同时仅 1 路输出;触发验证码时换号或稍后再试");
|
|
72
|
-
process.exit(0);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function printUsage() {
|
|
76
|
-
console.log("DeepSeek 登录(两种方式任选):");
|
|
77
|
-
console.log("");
|
|
78
|
-
console.log(" 方式 1(推荐,免密码):浏览器贴 token");
|
|
79
|
-
console.log(" 1. 浏览器登录 https://chat.deepseek.com");
|
|
80
|
-
console.log(" 2. F12 → Application → Local Storage → https://chat.deepseek.com");
|
|
81
|
-
console.log(" 3. 找到 userToken,复制值");
|
|
82
|
-
console.log(" 4. mslxdff -provider deepseek login --token <userToken>");
|
|
83
|
-
console.log("");
|
|
84
|
-
console.log(" 方式 2:账号密码(邮箱或手机号)");
|
|
85
|
-
console.log(" mslxdff -provider deepseek login you@example.com yourpassword");
|
|
86
|
-
console.log(" mslxdff -provider deepseek login 13800138000 yourpassword");
|
|
87
|
-
console.log("");
|
|
88
|
-
console.log(" 登录后:mslxdff -provider deepseek allowAny on && mslxdff -restart");
|
|
89
|
-
}
|