dsh-layered-memory 0.8.2 → 0.8.4
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.en.md +25 -18
- package/README.md +30 -17
- package/assets/readme/bench-dialog.svg +38 -49
- package/assets/readme/bench-workflow.svg +29 -29
- package/dist/client.js +387 -120
- package/dist/config.d.ts +11 -4
- package/dist/config.js +11 -3
- package/dist/index.d.ts +4 -4
- package/dist/index.js +4 -2
- package/dist/llm.d.ts +28 -2
- package/dist/llm.js +92 -8
- package/dist/pipeline/runner.js +6 -2
- package/dist/settings.d.ts +3 -2
- package/dist/settings.js +3 -3
- package/dist/stats.js +51 -15
- package/dist/store/download-queue.d.ts +3 -0
- package/dist/store/download-queue.js +22 -2
- package/dist/store/embedding-source.d.ts +1 -1
- package/dist/store/embedding-source.js +3 -3
- package/dist/store/io.d.ts +10 -2
- package/dist/store/io.js +28 -6
- package/dist/store/l0.js +7 -2
- package/dist/store/l1.js +17 -7
- package/dist/store/local-embedding.d.ts +3 -1
- package/dist/store/local-embedding.js +5 -2
- package/dist/store/runtime-installer.d.ts +8 -1
- package/dist/store/runtime-installer.js +34 -5
- package/dist/store/scenes.d.ts +2 -1
- package/dist/store/scenes.js +10 -1
- package/dist/store/sqlite.js +3 -3
- package/dist/tools/index.js +12 -2
- package/package.json +17 -17
package/dist/config.d.ts
CHANGED
|
@@ -5,6 +5,13 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import Schema from '@deepseek-ai/schemastery';
|
|
7
7
|
import type { ExtractMode } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* 蒸馏思考档位全词汇表(唯一事实源):'' = 自动(模型默认档 → high),
|
|
10
|
+
* 其余为各适配器通用档位词汇表(deepseek 认 'off',OpenAI 系是 'none')。
|
|
11
|
+
* schema(config/settings)、运行时解析(settings.resolveSettings)与 RPC
|
|
12
|
+
* 写入门(stats.settings-set)共用,勿在别处再抄字面量表。
|
|
13
|
+
*/
|
|
14
|
+
export declare const EFFORT_CHOICES: readonly ["", "off", "none", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
8
15
|
export interface MemoryConfig {
|
|
9
16
|
/** 数据目录;留空则用 $DSH_HOME/memory。 */
|
|
10
17
|
dataDir: string;
|
|
@@ -198,7 +205,7 @@ export declare const memorySchema: Schema<Schemastery.ObjectS<{
|
|
|
198
205
|
provider: Schema<string, string>;
|
|
199
206
|
model: Schema<string, string>;
|
|
200
207
|
maxTokens: Schema<number, number>;
|
|
201
|
-
reasoningEffort: Schema<"" | "off" | "high" | "max", "" | "off" | "high" | "max">;
|
|
208
|
+
reasoningEffort: Schema<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
|
|
202
209
|
temperature: Schema<number, number>;
|
|
203
210
|
maxInputChars: Schema<number, number>;
|
|
204
211
|
timeoutMs: Schema<number, number>;
|
|
@@ -206,7 +213,7 @@ export declare const memorySchema: Schema<Schemastery.ObjectS<{
|
|
|
206
213
|
provider: Schema<string, string>;
|
|
207
214
|
model: Schema<string, string>;
|
|
208
215
|
maxTokens: Schema<number, number>;
|
|
209
|
-
reasoningEffort: Schema<"" | "off" | "high" | "max", "" | "off" | "high" | "max">;
|
|
216
|
+
reasoningEffort: Schema<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
|
|
210
217
|
temperature: Schema<number, number>;
|
|
211
218
|
maxInputChars: Schema<number, number>;
|
|
212
219
|
timeoutMs: Schema<number, number>;
|
|
@@ -303,7 +310,7 @@ export declare const memorySchema: Schema<Schemastery.ObjectS<{
|
|
|
303
310
|
provider: Schema<string, string>;
|
|
304
311
|
model: Schema<string, string>;
|
|
305
312
|
maxTokens: Schema<number, number>;
|
|
306
|
-
reasoningEffort: Schema<"" | "off" | "high" | "max", "" | "off" | "high" | "max">;
|
|
313
|
+
reasoningEffort: Schema<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
|
|
307
314
|
temperature: Schema<number, number>;
|
|
308
315
|
maxInputChars: Schema<number, number>;
|
|
309
316
|
timeoutMs: Schema<number, number>;
|
|
@@ -311,7 +318,7 @@ export declare const memorySchema: Schema<Schemastery.ObjectS<{
|
|
|
311
318
|
provider: Schema<string, string>;
|
|
312
319
|
model: Schema<string, string>;
|
|
313
320
|
maxTokens: Schema<number, number>;
|
|
314
|
-
reasoningEffort: Schema<"" | "off" | "high" | "max", "" | "off" | "high" | "max">;
|
|
321
|
+
reasoningEffort: Schema<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
|
|
315
322
|
temperature: Schema<number, number>;
|
|
316
323
|
maxInputChars: Schema<number, number>;
|
|
317
324
|
timeoutMs: Schema<number, number>;
|
package/dist/config.js
CHANGED
|
@@ -5,6 +5,13 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import Schema from '@deepseek-ai/schemastery';
|
|
7
7
|
import { dshHomePath } from '@deepseek-ai/dsh-home-paths';
|
|
8
|
+
/**
|
|
9
|
+
* 蒸馏思考档位全词汇表(唯一事实源):'' = 自动(模型默认档 → high),
|
|
10
|
+
* 其余为各适配器通用档位词汇表(deepseek 认 'off',OpenAI 系是 'none')。
|
|
11
|
+
* schema(config/settings)、运行时解析(settings.resolveSettings)与 RPC
|
|
12
|
+
* 写入门(stats.settings-set)共用,勿在别处再抄字面量表。
|
|
13
|
+
*/
|
|
14
|
+
export const EFFORT_CHOICES = ['', 'off', 'none', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'];
|
|
8
15
|
export const memorySchema = Schema.object({
|
|
9
16
|
dataDir: Schema.string().default(''),
|
|
10
17
|
family: Schema.union(['auto', 'chat', 'work']).default('auto'),
|
|
@@ -61,9 +68,10 @@ export const memorySchema = Schema.object({
|
|
|
61
68
|
// 推理模型(如 v4-flash)的 reasoning 计入输出预算:预算不足会被思考吃光导致正文 0 字符。
|
|
62
69
|
// 0.8.0 起各蒸馏层显式传分层预算(见 llm.ts LAYER_MAX_TOKENS_*),本值为未分层调用的兜底总闸
|
|
63
70
|
maxTokens: Schema.number().min(1024).max(1_000_000).default(65_536),
|
|
64
|
-
//
|
|
65
|
-
//
|
|
66
|
-
|
|
71
|
+
// 蒸馏思考档位:'' = 自动(按模型能力解析:模型默认档 → high,见 llm.ts decideSendableEffort);
|
|
72
|
+
// 显式值仅在该模型声明支持时发送(跨供应商 effort 词汇表不同:deepseek 认 'off',
|
|
73
|
+
// openai 系是 'none',未声明档位的模型不传)。旧默认 'off' 在非 deepseek 模型上必炸(400/本地拒绝)
|
|
74
|
+
reasoningEffort: Schema.union([...EFFORT_CHOICES]).default(''),
|
|
67
75
|
temperature: Schema.number().min(0).max(2).default(0.3),
|
|
68
76
|
// 模型上下文 1M token,日常压到 ~700k 使用(中文按 1 字≈1 token 保守折算)
|
|
69
77
|
maxInputChars: Schema.number().min(1000).max(1_000_000).default(700_000),
|
package/dist/index.d.ts
CHANGED
|
@@ -99,7 +99,7 @@ export declare const Config: import("@deepseek-ai/schemastery").default<Schemast
|
|
|
99
99
|
provider: import("@deepseek-ai/schemastery").default<string, string>;
|
|
100
100
|
model: import("@deepseek-ai/schemastery").default<string, string>;
|
|
101
101
|
maxTokens: import("@deepseek-ai/schemastery").default<number, number>;
|
|
102
|
-
reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "high" | "max", "" | "off" | "high" | "max">;
|
|
102
|
+
reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
|
|
103
103
|
temperature: import("@deepseek-ai/schemastery").default<number, number>;
|
|
104
104
|
maxInputChars: import("@deepseek-ai/schemastery").default<number, number>;
|
|
105
105
|
timeoutMs: import("@deepseek-ai/schemastery").default<number, number>;
|
|
@@ -107,7 +107,7 @@ export declare const Config: import("@deepseek-ai/schemastery").default<Schemast
|
|
|
107
107
|
provider: import("@deepseek-ai/schemastery").default<string, string>;
|
|
108
108
|
model: import("@deepseek-ai/schemastery").default<string, string>;
|
|
109
109
|
maxTokens: import("@deepseek-ai/schemastery").default<number, number>;
|
|
110
|
-
reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "high" | "max", "" | "off" | "high" | "max">;
|
|
110
|
+
reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
|
|
111
111
|
temperature: import("@deepseek-ai/schemastery").default<number, number>;
|
|
112
112
|
maxInputChars: import("@deepseek-ai/schemastery").default<number, number>;
|
|
113
113
|
timeoutMs: import("@deepseek-ai/schemastery").default<number, number>;
|
|
@@ -204,7 +204,7 @@ export declare const Config: import("@deepseek-ai/schemastery").default<Schemast
|
|
|
204
204
|
provider: import("@deepseek-ai/schemastery").default<string, string>;
|
|
205
205
|
model: import("@deepseek-ai/schemastery").default<string, string>;
|
|
206
206
|
maxTokens: import("@deepseek-ai/schemastery").default<number, number>;
|
|
207
|
-
reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "high" | "max", "" | "off" | "high" | "max">;
|
|
207
|
+
reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
|
|
208
208
|
temperature: import("@deepseek-ai/schemastery").default<number, number>;
|
|
209
209
|
maxInputChars: import("@deepseek-ai/schemastery").default<number, number>;
|
|
210
210
|
timeoutMs: import("@deepseek-ai/schemastery").default<number, number>;
|
|
@@ -212,7 +212,7 @@ export declare const Config: import("@deepseek-ai/schemastery").default<Schemast
|
|
|
212
212
|
provider: import("@deepseek-ai/schemastery").default<string, string>;
|
|
213
213
|
model: import("@deepseek-ai/schemastery").default<string, string>;
|
|
214
214
|
maxTokens: import("@deepseek-ai/schemastery").default<number, number>;
|
|
215
|
-
reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "high" | "max", "" | "off" | "high" | "max">;
|
|
215
|
+
reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
|
|
216
216
|
temperature: import("@deepseek-ai/schemastery").default<number, number>;
|
|
217
217
|
maxInputChars: import("@deepseek-ai/schemastery").default<number, number>;
|
|
218
218
|
timeoutMs: import("@deepseek-ai/schemastery").default<number, number>;
|
package/dist/index.js
CHANGED
|
@@ -38,7 +38,7 @@ import { SessionModeStore } from './store/session-modes.js';
|
|
|
38
38
|
import { StateStore } from './store/state.js';
|
|
39
39
|
import { registerMemoryTools } from './tools/index.js';
|
|
40
40
|
import { errDetail, withFileLog } from './util/filelog.js';
|
|
41
|
-
import { resolveModelRoute } from './llm.js';
|
|
41
|
+
import { resolveModelRoute, invalidateEffortCache } from './llm.js';
|
|
42
42
|
import { effectiveCfg } from './pipeline/runner.js';
|
|
43
43
|
export const name = 'dsh-memory-plugin';
|
|
44
44
|
/** 硬依赖:蒸馏要用 llm,工具注册要用 tools,召回注入要用 systemPrompt。 */
|
|
@@ -60,6 +60,8 @@ export async function apply(ctx, config) {
|
|
|
60
60
|
// dsh 宿主无持久化日志,镜像 info+ 到数据目录 memory.log 供蒸馏问题诊断
|
|
61
61
|
const fileLogger = withFileLog(dataDir, logger);
|
|
62
62
|
logger = fileLogger;
|
|
63
|
+
// 供应商拓扑变化(增删/改配置)→ 思考档位能力缓存失效,下次调用重新探询
|
|
64
|
+
ctx.on('llm/adapters-updated', () => invalidateEffortCache());
|
|
63
65
|
// 存储初始化失败只降级(禁用捕获/蒸馏),绝不拖垮宿主——
|
|
64
66
|
// 记忆是增强能力,数据目录不可写时 dsh 本体必须照常启动。
|
|
65
67
|
let storageOk = true;
|
|
@@ -92,7 +94,7 @@ export async function apply(ctx, config) {
|
|
|
92
94
|
logger,
|
|
93
95
|
proxy: config.embedding.proxy,
|
|
94
96
|
});
|
|
95
|
-
const makeLocalService = makeLocalServiceFactory(installer, downloader, logger);
|
|
97
|
+
const makeLocalService = makeLocalServiceFactory(installer, downloader, logger, config.embedding.maxInputChars);
|
|
96
98
|
let initial = { svc: new NoopEmbeddingService(), dims: 0 };
|
|
97
99
|
/** 管理器引用:启动重嵌链/backfill 闭包在运行期解引用(声明早于创建避免 TDZ)。 */
|
|
98
100
|
let embedManagerRef;
|
package/dist/llm.d.ts
CHANGED
|
@@ -28,7 +28,7 @@ export declare const LAYER_DEFAULT_BUDGETS: Record<DistillBudgetLayer, number>;
|
|
|
28
28
|
/**
|
|
29
29
|
* 解析某蒸馏层的生效输出预算:运行时覆盖(cfg.llm.budgets,由 effectiveCfg 从
|
|
30
30
|
* 设置页 distillBudgets 注入,0/缺省 = 跟随)→ 内置默认 → 思考档放大
|
|
31
|
-
* (high/max ×4,reasoning 计入输出预算的历史事故防线)。
|
|
31
|
+
* (high/xhigh/max ×4,reasoning 计入输出预算的历史事故防线)。
|
|
32
32
|
*/
|
|
33
33
|
export declare function resolveLayerTokens(cfg: {
|
|
34
34
|
llm: {
|
|
@@ -36,9 +36,15 @@ export declare function resolveLayerTokens(cfg: {
|
|
|
36
36
|
budgets?: Partial<Record<DistillBudgetLayer, number>>;
|
|
37
37
|
};
|
|
38
38
|
}, layer: DistillBudgetLayer): number;
|
|
39
|
+
/**
|
|
40
|
+
* 高思考档集合(输出预算 ×4 的档位):阶段侧 layerMaxTokens 与 callLLM 的
|
|
41
|
+
* 自动档防线共用同一张表——此前两处字面量表分叉(防线漏 xhigh),显式 xhigh
|
|
42
|
+
* 配置被双重放大 ×16。勿再在别处抄写该列表。
|
|
43
|
+
*/
|
|
44
|
+
export declare const HIGH_EFFORT_TIERS: readonly ["high", "xhigh", "max"];
|
|
39
45
|
/**
|
|
40
46
|
* 思考档预算放大:reasoning 计入输出预算(v4-flash 事故:high 思考可吃光全部
|
|
41
|
-
* 预算致正文 0 字符)——effort 为 high/max 时分层预算 ×4。
|
|
47
|
+
* 预算致正文 0 字符)——effort 为 high/xhigh/max 时分层预算 ×4。
|
|
42
48
|
*/
|
|
43
49
|
export declare function layerMaxTokens(base: number, reasoningEffort: string): number;
|
|
44
50
|
/** 解析蒸馏用的 provider/model:配置优先,其次当前默认选择。 */
|
|
@@ -46,6 +52,26 @@ export declare function resolveModelRoute(ctx: Context, cfg: MemoryConfig): Prom
|
|
|
46
52
|
provider: string;
|
|
47
53
|
model: string;
|
|
48
54
|
}>;
|
|
55
|
+
export interface ModelEffortInfo {
|
|
56
|
+
/** 模型可设置的思考档位 id(适配器声明;空 = 未声明/不可设置) */
|
|
57
|
+
efforts: string[];
|
|
58
|
+
/** 适配器配置的默认档位(省略 effort 时的请求值) */
|
|
59
|
+
defaultEffort?: string;
|
|
60
|
+
}
|
|
61
|
+
/** 清空能力缓存(llm/adapters-updated 时调用:供应商增删/改配置后重新探询)。 */
|
|
62
|
+
export declare function invalidateEffortCache(): void;
|
|
63
|
+
/** 探询某模型的思考档位能力;失败返回 null(调用方保持旧发送行为,不改判)。 */
|
|
64
|
+
export declare function resolveModelEfforts(ctx: Context, provider: string, model: string): Promise<ModelEffortInfo | null>;
|
|
65
|
+
export type EffortDecisionReason = 'supported' | 'auto-default' | 'auto-high' | 'alias-none' | 'unsupported' | 'no-efforts' | 'no-capability';
|
|
66
|
+
export interface EffortDecision {
|
|
67
|
+
/** 实际发送的档位;'' = 不发送(跟随模型默认) */
|
|
68
|
+
effort: string;
|
|
69
|
+
reason: EffortDecisionReason;
|
|
70
|
+
}
|
|
71
|
+
/** 纯决策:配置档位 + 模型能力 → 实际发送值(callLLM 与 settings-get 共用)。 */
|
|
72
|
+
export declare function decideSendableEffort(cap: ModelEffortInfo | null, cfgEffort: string): EffortDecision;
|
|
73
|
+
/** 探询 + 决策 + 一次性告警(不支持/未声明时提示降级,不刷屏)。 */
|
|
74
|
+
export declare function planDistillEffort(ctx: Context, provider: string, model: string, cfgEffort: string, logger?: MemoryLogger): Promise<EffortDecision>;
|
|
49
75
|
/**
|
|
50
76
|
* 一次完整蒸馏调用:流式收集文本,返回最终字符串。
|
|
51
77
|
* 失败(error/aborted finish)抛错,由调用方兜底。
|
package/dist/llm.js
CHANGED
|
@@ -20,18 +20,24 @@ export const LAYER_DEFAULT_BUDGETS = {
|
|
|
20
20
|
/**
|
|
21
21
|
* 解析某蒸馏层的生效输出预算:运行时覆盖(cfg.llm.budgets,由 effectiveCfg 从
|
|
22
22
|
* 设置页 distillBudgets 注入,0/缺省 = 跟随)→ 内置默认 → 思考档放大
|
|
23
|
-
* (high/max ×4,reasoning 计入输出预算的历史事故防线)。
|
|
23
|
+
* (high/xhigh/max ×4,reasoning 计入输出预算的历史事故防线)。
|
|
24
24
|
*/
|
|
25
25
|
export function resolveLayerTokens(cfg, layer) {
|
|
26
26
|
const override = cfg.llm.budgets?.[layer];
|
|
27
27
|
return layerMaxTokens(override && override > 0 ? override : LAYER_DEFAULT_BUDGETS[layer], cfg.llm.reasoningEffort);
|
|
28
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* 高思考档集合(输出预算 ×4 的档位):阶段侧 layerMaxTokens 与 callLLM 的
|
|
31
|
+
* 自动档防线共用同一张表——此前两处字面量表分叉(防线漏 xhigh),显式 xhigh
|
|
32
|
+
* 配置被双重放大 ×16。勿再在别处抄写该列表。
|
|
33
|
+
*/
|
|
34
|
+
export const HIGH_EFFORT_TIERS = ['high', 'xhigh', 'max'];
|
|
29
35
|
/**
|
|
30
36
|
* 思考档预算放大:reasoning 计入输出预算(v4-flash 事故:high 思考可吃光全部
|
|
31
|
-
* 预算致正文 0 字符)——effort 为 high/max 时分层预算 ×4。
|
|
37
|
+
* 预算致正文 0 字符)——effort 为 high/xhigh/max 时分层预算 ×4。
|
|
32
38
|
*/
|
|
33
39
|
export function layerMaxTokens(base, reasoningEffort) {
|
|
34
|
-
return reasoningEffort
|
|
40
|
+
return HIGH_EFFORT_TIERS.includes(reasoningEffort) ? base * 4 : base;
|
|
35
41
|
}
|
|
36
42
|
/** 解析蒸馏用的 provider/model:配置优先,其次当前默认选择。 */
|
|
37
43
|
export async function resolveModelRoute(ctx, cfg) {
|
|
@@ -46,6 +52,75 @@ export async function resolveModelRoute(ctx, cfg) {
|
|
|
46
52
|
}
|
|
47
53
|
throw new Error('无法解析蒸馏模型路由:请在插件 config 中配置 llm.provider / llm.model,或确保存在默认模型选择');
|
|
48
54
|
}
|
|
55
|
+
const effortCache = new Map();
|
|
56
|
+
/** 清空能力缓存(llm/adapters-updated 时调用:供应商增删/改配置后重新探询)。 */
|
|
57
|
+
export function invalidateEffortCache() {
|
|
58
|
+
effortCache.clear();
|
|
59
|
+
effortWarned.clear();
|
|
60
|
+
}
|
|
61
|
+
/** 探询某模型的思考档位能力;失败返回 null(调用方保持旧发送行为,不改判)。 */
|
|
62
|
+
export async function resolveModelEfforts(ctx, provider, model) {
|
|
63
|
+
const key = `${provider}::${model}`;
|
|
64
|
+
const hit = effortCache.get(key);
|
|
65
|
+
if (hit)
|
|
66
|
+
return hit;
|
|
67
|
+
try {
|
|
68
|
+
if (typeof ctx.llm?.resolveModelInfo !== 'function')
|
|
69
|
+
return null;
|
|
70
|
+
const info = await ctx.llm.resolveModelInfo(provider, model);
|
|
71
|
+
const efforts = (info.reasoning?.efforts ?? [])
|
|
72
|
+
.map((e) => String(e.id))
|
|
73
|
+
.filter((id) => id.length > 0);
|
|
74
|
+
const cap = {
|
|
75
|
+
efforts,
|
|
76
|
+
...(info.reasoning?.defaultEffort ? { defaultEffort: String(info.reasoning.defaultEffort) } : {}),
|
|
77
|
+
};
|
|
78
|
+
effortCache.set(key, cap);
|
|
79
|
+
return cap;
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return null; // 不缓存失败:路由尚未注册等瞬时态,下次调用重试
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/** 纯决策:配置档位 + 模型能力 → 实际发送值(callLLM 与 settings-get 共用)。 */
|
|
86
|
+
export function decideSendableEffort(cap, cfgEffort) {
|
|
87
|
+
if (!cap)
|
|
88
|
+
return { effort: cfgEffort, reason: 'no-capability' };
|
|
89
|
+
if (cfgEffort) {
|
|
90
|
+
if (cap.efforts.includes(cfgEffort))
|
|
91
|
+
return { effort: cfgEffort, reason: 'supported' };
|
|
92
|
+
if (cfgEffort === 'off' && cap.efforts.includes('none'))
|
|
93
|
+
return { effort: 'none', reason: 'alias-none' };
|
|
94
|
+
if (cap.efforts.length === 0)
|
|
95
|
+
return { effort: '', reason: 'no-efforts' };
|
|
96
|
+
return { effort: '', reason: 'unsupported' };
|
|
97
|
+
}
|
|
98
|
+
// 空配置 = 自动:模型默认档 → 无默认取 high(用户规则:未声明/无默认一律 high)→ 仍无则不传
|
|
99
|
+
if (cap.defaultEffort && cap.efforts.includes(cap.defaultEffort)) {
|
|
100
|
+
return { effort: cap.defaultEffort, reason: 'auto-default' };
|
|
101
|
+
}
|
|
102
|
+
if (cap.efforts.includes('high'))
|
|
103
|
+
return { effort: 'high', reason: 'auto-high' };
|
|
104
|
+
return { effort: '', reason: 'no-efforts' };
|
|
105
|
+
}
|
|
106
|
+
const effortWarned = new Set();
|
|
107
|
+
/** 探询 + 决策 + 一次性告警(不支持/未声明时提示降级,不刷屏)。 */
|
|
108
|
+
export async function planDistillEffort(ctx, provider, model, cfgEffort, logger) {
|
|
109
|
+
const cap = await resolveModelEfforts(ctx, provider, model);
|
|
110
|
+
const d = decideSendableEffort(cap, cfgEffort);
|
|
111
|
+
if ((d.reason === 'unsupported' || d.reason === 'no-efforts') && logger) {
|
|
112
|
+
const key = `${provider}::${model}::${cfgEffort}::${d.reason}`;
|
|
113
|
+
if (!effortWarned.has(key)) {
|
|
114
|
+
effortWarned.add(key);
|
|
115
|
+
logger.warn(`[memory] 蒸馏思考档位 ${cfgEffort || '(auto)'} 不被 ${provider}/${model} 支持` +
|
|
116
|
+
(d.reason === 'no-efforts'
|
|
117
|
+
? '(模型未声明思考档位)'
|
|
118
|
+
: `(支持: ${cap?.efforts.join('/')})`) +
|
|
119
|
+
',本次调用不传档位(跟随模型默认)');
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return d;
|
|
123
|
+
}
|
|
49
124
|
/**
|
|
50
125
|
* 一次完整蒸馏调用:流式收集文本,返回最终字符串。
|
|
51
126
|
* 失败(error/aborted finish)抛错,由调用方兜底。
|
|
@@ -56,22 +131,31 @@ export async function resolveModelRoute(ctx, cfg) {
|
|
|
56
131
|
export async function callLLM(ctx, cfg, opts) {
|
|
57
132
|
const { provider, model } = await resolveModelRoute(ctx, cfg);
|
|
58
133
|
const signal = opts.signal ?? AbortSignal.timeout(cfg.llm.timeoutMs);
|
|
134
|
+
// 档位按模型能力决策(跨供应商 effort 兼容):不支持的档位不传 + 告警一次,
|
|
135
|
+
// 空配置 = 自动(模型默认档 → high);详见 decideSendableEffort
|
|
136
|
+
const effort = await planDistillEffort(ctx, provider, model, cfg.llm.reasoningEffort, opts.logger);
|
|
59
137
|
// 输入预算兜底:任何蒸馏调用的用户 prompt 不超过 maxInputChars
|
|
60
138
|
// (L1 已在数据层分块,这里是 L2/L3 与异常场景的最后一道网)
|
|
61
139
|
const user = opts.user.length > cfg.llm.maxInputChars
|
|
62
140
|
? `${opts.user.slice(0, cfg.llm.maxInputChars)}\n\n[输入超出 ${cfg.llm.maxInputChars} 字符预算,已截断]`
|
|
63
141
|
: opts.user;
|
|
142
|
+
// 输出预算 ×4 防线跟随【实际发送】的档位:阶段侧已按原始配置的高档位
|
|
143
|
+
// (HIGH_EFFORT_TIERS)放大过,这里只补自动档('' → 模型默认/高档)解析出
|
|
144
|
+
// 高档时的欠放大缺口——两侧共用一张表,配置本身就是高档时不再放大(防 ×16 双乘)
|
|
145
|
+
const baseMaxTokens = opts.maxTokens ?? cfg.llm.maxTokens;
|
|
146
|
+
const highTiers = HIGH_EFFORT_TIERS;
|
|
147
|
+
const maxTokens = highTiers.includes(effort.effort) && !highTiers.includes(cfg.llm.reasoningEffort)
|
|
148
|
+
? layerMaxTokens(baseMaxTokens, 'high')
|
|
149
|
+
: baseMaxTokens;
|
|
64
150
|
const stream = ctx.llm.stream({
|
|
65
151
|
provider,
|
|
66
152
|
model,
|
|
67
153
|
system: opts.system,
|
|
68
154
|
messages: [createUserMessage({ content: [{ type: 'text', text: user }], source: { kind: 'user' } })],
|
|
69
155
|
temperature: opts.temperature ?? cfg.llm.temperature,
|
|
70
|
-
maxTokens
|
|
71
|
-
//
|
|
72
|
-
...(
|
|
73
|
-
? { reasoningEffort: ReasoningEffortId(cfg.llm.reasoningEffort) }
|
|
74
|
-
: {}),
|
|
156
|
+
maxTokens,
|
|
157
|
+
// 档位只在能力决策给出非空值时传;空串不传(跟随模型默认)
|
|
158
|
+
...(effort.effort ? { reasoningEffort: ReasoningEffortId(effort.effort) } : {}),
|
|
75
159
|
signal,
|
|
76
160
|
});
|
|
77
161
|
const startedAt = Date.now();
|
package/dist/pipeline/runner.js
CHANGED
|
@@ -25,6 +25,8 @@ export function pickNextTaskIndex(tasks) {
|
|
|
25
25
|
*/
|
|
26
26
|
export function effectiveCfg(cfg, live) {
|
|
27
27
|
const s = live?.get();
|
|
28
|
+
// 思考档位:设置服务在场时运行时值整体接管——'' = 自动(按模型能力解析),
|
|
29
|
+
// 不再回退静态配置("跟随配置"选项已删);静态值仅无 settings 服务的部署生效
|
|
28
30
|
const eff = s?.reasoningEffort ?? '';
|
|
29
31
|
// 可选链防御:smoke/测试缝构造的最小 cfg 可能没有 llm 字段
|
|
30
32
|
const pinned = Boolean(cfg.llm?.provider && cfg.llm?.model);
|
|
@@ -41,13 +43,15 @@ export function effectiveCfg(cfg, live) {
|
|
|
41
43
|
}
|
|
42
44
|
: null;
|
|
43
45
|
const maxInput = s && s.distillMaxInputChars > 0 ? s.distillMaxInputChars : null;
|
|
44
|
-
|
|
46
|
+
// 无任何注入且(无 live,或运行时 '' 且静态本就 '')→ 原引用返回,保持引用稳定性
|
|
47
|
+
const effNoop = eff === '' && (!live || !cfg.llm?.reasoningEffort);
|
|
48
|
+
if (!override && !budgets && !maxInput && effNoop)
|
|
45
49
|
return cfg;
|
|
46
50
|
return {
|
|
47
51
|
...cfg,
|
|
48
52
|
llm: {
|
|
49
53
|
...cfg.llm,
|
|
50
|
-
...(
|
|
54
|
+
...(live ? { reasoningEffort: eff } : {}),
|
|
51
55
|
...(override ?? {}),
|
|
52
56
|
...(budgets ? { budgets } : {}),
|
|
53
57
|
...(maxInput ? { maxInputChars: maxInput } : {}),
|
package/dist/settings.d.ts
CHANGED
|
@@ -5,10 +5,11 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import type { Context } from '@deepseek-ai/cordis';
|
|
7
7
|
import Schema from '@deepseek-ai/schemastery';
|
|
8
|
+
import { EFFORT_CHOICES } from './config.js';
|
|
8
9
|
import type { DistillBudgetLayer } from './llm.js';
|
|
9
10
|
import type { MemoryLogger } from './types.js';
|
|
10
|
-
/**
|
|
11
|
-
export type EffortChoice =
|
|
11
|
+
/** 蒸馏思考档位:'' = 自动(模型默认档 → high);词汇表单源于 config.ts 的 EFFORT_CHOICES。 */
|
|
12
|
+
export type EffortChoice = (typeof EFFORT_CHOICES)[number];
|
|
12
13
|
/** 分层输出预算(与 llm.ts 的 DistillBudgetLayer 同键;0 = 跟随内置默认)。 */
|
|
13
14
|
export type DistillBudgets = Record<DistillBudgetLayer, number>;
|
|
14
15
|
export interface MemoryLiveSettings {
|
package/dist/settings.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import Schema from '@deepseek-ai/schemastery';
|
|
2
2
|
import { settingsNamespace } from '@deepseek-ai/dsh-settings';
|
|
3
|
+
import { EFFORT_CHOICES } from './config.js';
|
|
3
4
|
const NS = settingsNamespace('dsh-memory');
|
|
4
5
|
const ALWAYS_ON = {
|
|
5
6
|
enabled: true,
|
|
@@ -36,7 +37,7 @@ export function liveSettingsSchema() {
|
|
|
36
37
|
capture: Schema.boolean().default(true),
|
|
37
38
|
distill: Schema.boolean().default(true),
|
|
38
39
|
recall: Schema.boolean().default(true),
|
|
39
|
-
reasoningEffort: Schema.union([
|
|
40
|
+
reasoningEffort: Schema.union([...EFFORT_CHOICES]).default(''),
|
|
40
41
|
distillProvider: Schema.string().default(''),
|
|
41
42
|
distillModel: Schema.string().default(''),
|
|
42
43
|
distillBudgets: Schema.object({
|
|
@@ -154,7 +155,6 @@ function resolveSettings(value) {
|
|
|
154
155
|
if (!value || typeof value !== 'object')
|
|
155
156
|
return { ...ALWAYS_ON };
|
|
156
157
|
const v = value;
|
|
157
|
-
const efforts = ['', 'off', 'high', 'max'];
|
|
158
158
|
const num = (x) => (typeof x === 'number' && Number.isFinite(x) && x >= 0 ? Math.floor(x) : 0);
|
|
159
159
|
const rawBudgets = (v.distillBudgets ?? {});
|
|
160
160
|
return {
|
|
@@ -162,7 +162,7 @@ function resolveSettings(value) {
|
|
|
162
162
|
capture: v.capture !== false,
|
|
163
163
|
distill: v.distill !== false,
|
|
164
164
|
recall: v.recall !== false,
|
|
165
|
-
reasoningEffort: typeof v.reasoningEffort === 'string' &&
|
|
165
|
+
reasoningEffort: typeof v.reasoningEffort === 'string' && EFFORT_CHOICES.includes(v.reasoningEffort)
|
|
166
166
|
? v.reasoningEffort
|
|
167
167
|
: '',
|
|
168
168
|
distillProvider: typeof v.distillProvider === 'string' ? v.distillProvider : '',
|
package/dist/stats.js
CHANGED
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
import { createRequire } from 'node:module';
|
|
10
10
|
import { closeSync, openSync, readSync, statSync } from 'node:fs';
|
|
11
11
|
import { join } from 'node:path';
|
|
12
|
-
import { resolveDataDir } from './config.js';
|
|
12
|
+
import { EFFORT_CHOICES, resolveDataDir } from './config.js';
|
|
13
13
|
import { effectiveCfg } from './pipeline/runner.js';
|
|
14
|
-
import { LAYER_DEFAULT_BUDGETS, resolveModelRoute } from './llm.js';
|
|
14
|
+
import { decideSendableEffort, LAYER_DEFAULT_BUDGETS, resolveModelEfforts, resolveModelRoute } from './llm.js';
|
|
15
15
|
import { errDetail } from './util/filelog.js';
|
|
16
16
|
const require = createRequire(import.meta.url);
|
|
17
17
|
export const PLUGIN_VERSION = require('../package.json').version;
|
|
@@ -132,6 +132,15 @@ async function buildStats(cfg, stores, status) {
|
|
|
132
132
|
thresholds: { l2MinNewMemories: cfg.l2.minNewMemories, l3Interval: cfg.l3.interval },
|
|
133
133
|
};
|
|
134
134
|
}
|
|
135
|
+
/** RPC 字符串入参上限校验:防 loopback 面畸形超长载荷
|
|
136
|
+
* (超长 sessionId 持久化进 session-modes.json / 超长 query 触发 jieba 全量分词 CPU 峰值)。 */
|
|
137
|
+
function expectSessionId(v) {
|
|
138
|
+
if (typeof v !== 'string' || !v)
|
|
139
|
+
throw new Error('sessionId 缺失');
|
|
140
|
+
if (v.length > 512)
|
|
141
|
+
throw new Error('sessionId 过长(≤512 字符)');
|
|
142
|
+
return v;
|
|
143
|
+
}
|
|
135
144
|
async function handleEndpoint(endpoint, payload, deps) {
|
|
136
145
|
const { cfg, stores, status, live, modes, dataDir, rebuild, embedManager } = deps;
|
|
137
146
|
switch (endpoint) {
|
|
@@ -141,27 +150,44 @@ async function handleEndpoint(endpoint, payload, deps) {
|
|
|
141
150
|
if (!modes)
|
|
142
151
|
throw new Error('档位存储未初始化');
|
|
143
152
|
const p = (payload ?? {});
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
return { sessionId: p.sessionId, mode: modes.get(p.sessionId), defaultMode: modes.default };
|
|
153
|
+
const sessionId = expectSessionId(p.sessionId);
|
|
154
|
+
return { sessionId, mode: modes.get(sessionId), defaultMode: modes.default };
|
|
147
155
|
}
|
|
148
156
|
case 'dsh-memory/session-mode-set': {
|
|
149
157
|
if (!modes)
|
|
150
158
|
throw new Error('档位存储未初始化');
|
|
151
159
|
const p = (payload ?? {});
|
|
152
|
-
|
|
153
|
-
throw new Error('sessionId 缺失');
|
|
160
|
+
const sessionId = expectSessionId(p.sessionId);
|
|
154
161
|
const allowed = ['auto', 'chat', 'work', 'off'];
|
|
155
162
|
if (typeof p.mode !== 'string' || !allowed.includes(p.mode)) {
|
|
156
163
|
throw new Error(`非法档位: ${String(p.mode)}(允许 ${allowed.join('/')})`);
|
|
157
164
|
}
|
|
158
|
-
modes.set(
|
|
159
|
-
deps.logger.info(`[memory] 会话档位设置 session=${
|
|
160
|
-
return { sessionId
|
|
165
|
+
modes.set(sessionId, p.mode);
|
|
166
|
+
deps.logger.info(`[memory] 会话档位设置 session=${sessionId} mode=${p.mode}`);
|
|
167
|
+
return { sessionId, mode: p.mode };
|
|
161
168
|
}
|
|
162
169
|
case 'dsh-memory/settings-get': {
|
|
163
170
|
const s = live?.get();
|
|
164
171
|
const budgets = s?.distillBudgets ?? { extract: 0, dedup: 0, l2: 0, l3: 0 };
|
|
172
|
+
// 蒸馏思考档位:current 是运行时值('' = 自动);effective 是能力探询后实际发送值
|
|
173
|
+
// ('' = 不传,跟随模型默认);options 是当前生效模型声明的档位表(空声明 → 只显示
|
|
174
|
+
// high,用户规则:无声明默认 high),供蒸馏思考选择器渲染;fallback 是静态部署值
|
|
175
|
+
let effortEffective = s?.reasoningEffort || cfg.llm.reasoningEffort;
|
|
176
|
+
let effortOptions = ['high'];
|
|
177
|
+
let effortRoute = null;
|
|
178
|
+
try {
|
|
179
|
+
const ecfg = effectiveCfg(cfg, live);
|
|
180
|
+
effortRoute = await resolveModelRoute(deps.ctx, ecfg);
|
|
181
|
+
const cap = await resolveModelEfforts(deps.ctx, effortRoute.provider, effortRoute.model);
|
|
182
|
+
if (cap) {
|
|
183
|
+
effortEffective = decideSendableEffort(cap, ecfg.llm.reasoningEffort).effort;
|
|
184
|
+
if (cap.efforts.length > 0)
|
|
185
|
+
effortOptions = cap.efforts;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
/* 路由解析/探询失败保持占位(effective 用运行时||静态值) */
|
|
190
|
+
}
|
|
165
191
|
return {
|
|
166
192
|
supported: live?.supported ?? false,
|
|
167
193
|
settings: s ?? {
|
|
@@ -171,11 +197,12 @@ async function handleEndpoint(endpoint, payload, deps) {
|
|
|
171
197
|
},
|
|
172
198
|
// 静态部署上限(cordis.patch.yml):运行时开关与它取 AND
|
|
173
199
|
ceilings: { capture: cfg.capture.enabled, distill: cfg.extract.enabled, recall: cfg.recall.enabled },
|
|
174
|
-
// 蒸馏思考档位:current 是运行时覆盖('' = 跟随配置),effective 是实际生效值
|
|
175
200
|
effort: {
|
|
176
201
|
current: s?.reasoningEffort ?? '',
|
|
177
|
-
effective:
|
|
202
|
+
effective: effortEffective,
|
|
178
203
|
fallback: cfg.llm.reasoningEffort,
|
|
204
|
+
options: effortOptions,
|
|
205
|
+
...(effortRoute ? { route: effortRoute } : {}),
|
|
179
206
|
},
|
|
180
207
|
// 分层输出预算:current 是运行时覆盖(0 = 跟随默认),defaults 是内置默认(UI 占位/提示用)
|
|
181
208
|
budgets: {
|
|
@@ -207,8 +234,10 @@ async function handleEndpoint(endpoint, payload, deps) {
|
|
|
207
234
|
}
|
|
208
235
|
if (patch.reasoningEffort !== undefined) {
|
|
209
236
|
const v = String(patch.reasoningEffort);
|
|
210
|
-
|
|
211
|
-
|
|
237
|
+
// 白名单与 schema/settings 同源(config.ts EFFORT_CHOICES)——此前此处漏扩词表,
|
|
238
|
+
// 设置页新词汇(none/minimal/low/medium/xhigh)被拒并回滚
|
|
239
|
+
if (!EFFORT_CHOICES.includes(v)) {
|
|
240
|
+
throw new Error(`非法思考档位: ${v}(允许 '' 或 ${EFFORT_CHOICES.filter((x) => x !== '').join('/')})`);
|
|
212
241
|
}
|
|
213
242
|
clean.reasoningEffort = v;
|
|
214
243
|
}
|
|
@@ -251,8 +280,10 @@ async function handleEndpoint(endpoint, payload, deps) {
|
|
|
251
280
|
}
|
|
252
281
|
case 'dsh-memory/list-records': {
|
|
253
282
|
const p = (payload ?? {});
|
|
283
|
+
if (p.query !== undefined && p.query.length > 4096)
|
|
284
|
+
throw new Error('query 过长(≤4096 字符)');
|
|
254
285
|
const limit = Math.min(Math.max(Number(p.limit) || 50, 1), 200);
|
|
255
|
-
const offset = Math.max(Number(p.offset) || 0, 0);
|
|
286
|
+
const offset = Math.min(Math.max(Number(p.offset) || 0, 0), 1_000_000);
|
|
256
287
|
// 关键词路径:复用检索唯一缝(与召回同源),取回后做场景过滤 + 手工分页。
|
|
257
288
|
// 检索侧单次上限 200:分页窗口触达上限时显式标记 truncated(结果可能不完整),
|
|
258
289
|
// 不再静默返回空结果让用户误以为"没有更多"等于"不存在更多"。
|
|
@@ -371,6 +402,8 @@ async function handleEndpoint(endpoint, payload, deps) {
|
|
|
371
402
|
const p = (payload ?? {});
|
|
372
403
|
if (typeof p.provider !== 'string' || !p.provider)
|
|
373
404
|
throw new Error('provider 缺失');
|
|
405
|
+
if (p.provider.length > 200)
|
|
406
|
+
throw new Error('provider 过长(≤200 字符)');
|
|
374
407
|
// 两个内置适配器(deepseek/pi-ai)的 listModels 都读本地快照不触网;
|
|
375
408
|
// 仍加超时兜底,防第三方适配器实现为远端查询拖死 RPC 轮询
|
|
376
409
|
const models = await Promise.race([
|
|
@@ -395,6 +428,9 @@ async function handleEndpoint(endpoint, payload, deps) {
|
|
|
395
428
|
if (p.source !== 'remote' && p.source !== 'local' && p.source !== 'off') {
|
|
396
429
|
throw new Error('source 必须是 remote | local | off');
|
|
397
430
|
}
|
|
431
|
+
if (typeof p.activeModel === 'string' && p.activeModel.length > 200) {
|
|
432
|
+
throw new Error('activeModel 过长(≤200 字符)');
|
|
433
|
+
}
|
|
398
434
|
const r = embedManager.requestSource({ source: p.source, activeModel: p.activeModel ?? null });
|
|
399
435
|
if (!r.accepted)
|
|
400
436
|
throw new Error(r.error ?? '切换请求被拒绝');
|
|
@@ -101,4 +101,7 @@ export declare class ModelDownloadQueue {
|
|
|
101
101
|
* 镜像直连往往间歇不可达(真实事故:直连超时与污染字节交替出现)。
|
|
102
102
|
*/
|
|
103
103
|
export declare function resolveProxyUrl(setting: string | undefined, host: string): string;
|
|
104
|
+
/** 代理 URL 日志脱敏:剥掉 userinfo(内网代理常带 user:pass 凭据),只留 scheme//host;
|
|
105
|
+
* 解析失败的串原样也可能是凭据形态,返回占位符。 */
|
|
106
|
+
export declare function maskProxyUrl(proxy: string): string;
|
|
104
107
|
export {};
|
|
@@ -40,8 +40,15 @@ export class ModelDownloadQueue {
|
|
|
40
40
|
}
|
|
41
41
|
const proxy = resolveProxyUrl(opts.proxy, host);
|
|
42
42
|
if (proxy) {
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
try {
|
|
44
|
+
this.agent = new ProxyAgent(proxy);
|
|
45
|
+
opts.logger?.info(`[memory] 模型下载走代理 ${maskProxyUrl(proxy)}(镜像直连在国内网络间歇不可达)`);
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
// 与畸形 mirror 同款容错(见上):畸形代理(无 scheme 等常见笔误)只降级直连,
|
|
49
|
+
// 绝不炸构造器——本构造发生在 apply 装配链上,抛错会拖垮宿主启动
|
|
50
|
+
opts.logger?.warn(`[memory] 代理配置无效,已忽略并直连(${maskProxyUrl(proxy)}): ${err instanceof Error ? err.message : String(err)}`);
|
|
51
|
+
}
|
|
45
52
|
}
|
|
46
53
|
this.defaultFetch = ((u, init) => {
|
|
47
54
|
const dispatch = this.agent;
|
|
@@ -382,6 +389,8 @@ export function resolveProxyUrl(setting, host) {
|
|
|
382
389
|
if (value)
|
|
383
390
|
return value;
|
|
384
391
|
const noProxy = process.env.NO_PROXY ?? process.env.no_proxy ?? '';
|
|
392
|
+
if (noProxy.trim() === '*')
|
|
393
|
+
return '';
|
|
385
394
|
if (noProxy) {
|
|
386
395
|
for (const raw of noProxy.split(',')) {
|
|
387
396
|
const entry = raw.trim().replace(/^\./, '').toLowerCase();
|
|
@@ -400,6 +409,17 @@ export function resolveProxyUrl(setting, host) {
|
|
|
400
409
|
}
|
|
401
410
|
return '';
|
|
402
411
|
}
|
|
412
|
+
/** 代理 URL 日志脱敏:剥掉 userinfo(内网代理常带 user:pass 凭据),只留 scheme//host;
|
|
413
|
+
* 解析失败的串原样也可能是凭据形态,返回占位符。 */
|
|
414
|
+
export function maskProxyUrl(proxy) {
|
|
415
|
+
try {
|
|
416
|
+
const u = new URL(proxy);
|
|
417
|
+
return `${u.protocol}//${u.host}`;
|
|
418
|
+
}
|
|
419
|
+
catch {
|
|
420
|
+
return '<invalid-url>';
|
|
421
|
+
}
|
|
422
|
+
}
|
|
403
423
|
async function sha256File(p) {
|
|
404
424
|
const { createReadStream } = await import('node:fs');
|
|
405
425
|
const hash = createHash('sha256');
|
|
@@ -36,7 +36,7 @@ export interface InitialEmbedding {
|
|
|
36
36
|
export declare function remoteCeiling(cfg: MemoryConfig): boolean;
|
|
37
37
|
export declare function resolveInitialEmbedding(cfg: MemoryConfig, sourceStore: EmbeddingSourceStore, downloader: ModelDownloadQueue, makeLocal: (modelId: string) => LocalEmbeddingService | null, logger?: MemoryLogger): Promise<InitialEmbedding>;
|
|
38
38
|
/** 本地服务构造工厂(index.ts 的初始解析与 Manager 共用一份实现,防漂移)。 */
|
|
39
|
-
export declare function makeLocalServiceFactory(installer: RuntimeInstaller, downloader: ModelDownloadQueue, logger?: MemoryLogger): (modelId: string) => LocalEmbeddingService | null;
|
|
39
|
+
export declare function makeLocalServiceFactory(installer: RuntimeInstaller, downloader: ModelDownloadQueue, logger?: MemoryLogger, maxInputChars?: number): (modelId: string) => LocalEmbeddingService | null;
|
|
40
40
|
export type ApplyPhase = 'idle' | 'installing-runtime' | 'warming' | 'switching' | 'reindexing' | 'done' | 'error';
|
|
41
41
|
export interface ReindexProgressState {
|
|
42
42
|
running: boolean;
|