dsh-prime-memory 0.11.0
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/CHANGELOG.en.md +28 -0
- package/CHANGELOG.ja.md +30 -0
- package/CHANGELOG.ko.md +30 -0
- package/CHANGELOG.md +1220 -0
- package/ENGINEERING-NOTES.md +452 -0
- package/INSTALL.en.md +92 -0
- package/INSTALL.ja.md +92 -0
- package/INSTALL.ko.md +92 -0
- package/INSTALL.md +92 -0
- package/LICENSE +21 -0
- package/README.en.md +458 -0
- package/README.ja.md +306 -0
- package/README.ko.md +306 -0
- package/README.md +425 -0
- package/assets/changelog/0.8.10/01-write-only-pill.png +0 -0
- package/assets/changelog/0.8.9/01-panel.png +0 -0
- package/assets/changelog/0.8.9/02-halo.png +0 -0
- package/assets/changelog/0.8.9/03-layer-segmented-panel.png +0 -0
- package/assets/changelog/0.8.9/04-layer-l1-panel.png +0 -0
- package/assets/img/EmbeddingSource.png +0 -0
- package/assets/img/Hero.png +0 -0
- package/assets/img/Layers.png +0 -0
- package/assets/img/MemoryTools.png +0 -0
- package/assets/img/Modes.png +0 -0
- package/assets/img/ToolTrajectory.png +0 -0
- package/assets/img/ui-dark.jpg +0 -0
- package/assets/img/ui-light.jpg +0 -0
- package/assets/readme/bench-dialog.svg +70 -0
- package/assets/readme/bench-workflow.svg +79 -0
- package/assets/readme/flow.svg +189 -0
- package/assets/readme/storage.svg +115 -0
- package/cordis.patch.yml +16 -0
- package/dist/bench-control.d.ts +34 -0
- package/dist/bench-control.js +16 -0
- package/dist/client.js +4293 -0
- package/dist/config.d.ts +683 -0
- package/dist/config.js +129 -0
- package/dist/contract.d.ts +820 -0
- package/dist/contract.js +1 -0
- package/dist/embedding-worker.cjs +176 -0
- package/dist/graph/apply.d.ts +37 -0
- package/dist/graph/apply.js +270 -0
- package/dist/graph/constraints.d.ts +47 -0
- package/dist/graph/constraints.js +38 -0
- package/dist/graph/search.d.ts +16 -0
- package/dist/graph/search.js +115 -0
- package/dist/graph/types.d.ts +142 -0
- package/dist/graph/types.js +14 -0
- package/dist/hooks/capture.d.ts +32 -0
- package/dist/hooks/capture.js +194 -0
- package/dist/hooks/recall.d.ts +63 -0
- package/dist/hooks/recall.js +429 -0
- package/dist/index.d.ts +534 -0
- package/dist/index.js +344 -0
- package/dist/llm-usage.d.ts +26 -0
- package/dist/llm-usage.js +37 -0
- package/dist/llm.d.ts +153 -0
- package/dist/llm.js +530 -0
- package/dist/pipeline/graph.d.ts +35 -0
- package/dist/pipeline/graph.js +104 -0
- package/dist/pipeline/l1.d.ts +19 -0
- package/dist/pipeline/l1.js +271 -0
- package/dist/pipeline/l2.d.ts +13 -0
- package/dist/pipeline/l2.js +83 -0
- package/dist/pipeline/l3.d.ts +15 -0
- package/dist/pipeline/l3.js +78 -0
- package/dist/pipeline/rebuild.d.ts +61 -0
- package/dist/pipeline/rebuild.js +307 -0
- package/dist/pipeline/ruminate.d.ts +89 -0
- package/dist/pipeline/ruminate.js +298 -0
- package/dist/pipeline/runner.d.ts +167 -0
- package/dist/pipeline/runner.js +638 -0
- package/dist/pipeline/trigger.d.ts +40 -0
- package/dist/pipeline/trigger.js +75 -0
- package/dist/prompts/graph-projection.d.ts +70 -0
- package/dist/prompts/graph-projection.js +167 -0
- package/dist/prompts/l1-dedup.d.ts +22 -0
- package/dist/prompts/l1-dedup.js +251 -0
- package/dist/prompts/l1-extraction.d.ts +22 -0
- package/dist/prompts/l1-extraction.js +457 -0
- package/dist/prompts/persona.d.ts +23 -0
- package/dist/prompts/persona.js +240 -0
- package/dist/prompts/scene.d.ts +32 -0
- package/dist/prompts/scene.js +414 -0
- package/dist/runtime-package-lock.json +982 -0
- package/dist/settings.d.ts +50 -0
- package/dist/settings.js +355 -0
- package/dist/stats.d.ts +109 -0
- package/dist/stats.js +929 -0
- package/dist/store/bm25.d.ts +19 -0
- package/dist/store/bm25.js +63 -0
- package/dist/store/cost-ledger.d.ts +75 -0
- package/dist/store/cost-ledger.js +171 -0
- package/dist/store/download-queue.d.ts +79 -0
- package/dist/store/download-queue.js +424 -0
- package/dist/store/embedding-source.d.ts +118 -0
- package/dist/store/embedding-source.js +443 -0
- package/dist/store/embedding.d.ts +90 -0
- package/dist/store/embedding.js +206 -0
- package/dist/store/graph-store.d.ts +94 -0
- package/dist/store/graph-store.js +641 -0
- package/dist/store/l0.d.ts +40 -0
- package/dist/store/l0.js +197 -0
- package/dist/store/l1.d.ts +93 -0
- package/dist/store/l1.js +297 -0
- package/dist/store/local-embedding.d.ts +89 -0
- package/dist/store/local-embedding.js +227 -0
- package/dist/store/model-catalog.d.ts +48 -0
- package/dist/store/model-catalog.js +81 -0
- package/dist/store/occupancy.d.ts +30 -0
- package/dist/store/occupancy.js +134 -0
- package/dist/store/pending.d.ts +36 -0
- package/dist/store/pending.js +103 -0
- package/dist/store/persona.d.ts +15 -0
- package/dist/store/persona.js +60 -0
- package/dist/store/recall-dedupe.d.ts +26 -0
- package/dist/store/recall-dedupe.js +138 -0
- package/dist/store/runtime-installer.d.ts +59 -0
- package/dist/store/runtime-installer.js +243 -0
- package/dist/store/scenes.d.ts +24 -0
- package/dist/store/scenes.js +160 -0
- package/dist/store/search-utils.d.ts +38 -0
- package/dist/store/search-utils.js +100 -0
- package/dist/store/session-modes.d.ts +35 -0
- package/dist/store/session-modes.js +144 -0
- package/dist/store/sqlite.d.ts +246 -0
- package/dist/store/sqlite.js +1491 -0
- package/dist/store/state.d.ts +41 -0
- package/dist/store/state.js +72 -0
- package/dist/token-cost.d.ts +23 -0
- package/dist/token-cost.js +185 -0
- package/dist/tools/index.d.ts +34 -0
- package/dist/tools/index.js +758 -0
- package/dist/types.d.ts +139 -0
- package/dist/types.js +38 -0
- package/dist/util/context-occupancy.d.ts +68 -0
- package/dist/util/context-occupancy.js +92 -0
- package/dist/util/filelog.d.ts +6 -0
- package/dist/util/filelog.js +108 -0
- package/dist/util/io.d.ts +18 -0
- package/dist/util/io.js +97 -0
- package/dist/util/recall-budget.d.ts +31 -0
- package/dist/util/recall-budget.js +84 -0
- package/dist/util/sanitize.d.ts +11 -0
- package/dist/util/sanitize.js +67 -0
- package/dist/util/text.d.ts +16 -0
- package/dist/util/text.js +61 -0
- package/dist/util/tokenizer.d.ts +9 -0
- package/dist/util/tokenizer.js +50 -0
- package/dsh.plugin.json +22 -0
- package/package.json +118 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-memory-plugin — L0~L3 分层蒸馏记忆插件(持久组合插件)。
|
|
3
|
+
*
|
|
4
|
+
* 装配职责:数据目录与文件日志 → live 开关 → 会话档位 → 嵌入源三态解析 →
|
|
5
|
+
* 检索引擎(memory.db)与分族存储 → 蒸馏 runner/重建控制器 → 捕获/召回钩子 →
|
|
6
|
+
* 工具/RPC/bench 控制面 → 停机顺序。
|
|
7
|
+
*
|
|
8
|
+
* 存储架构(JSONL 事实源 + memory.db 主检索):见 README 与 store 层模块注释。
|
|
9
|
+
* 降级铁律:存储初始化失败只停用捕获/蒸馏,绝不拖垮宿主启动。
|
|
10
|
+
*/
|
|
11
|
+
import * as path from 'node:path';
|
|
12
|
+
import { memorySchema, resolveDataDir } from './config.js';
|
|
13
|
+
import { registerBenchControl } from './bench-control.js';
|
|
14
|
+
import { registerCapture } from './hooks/capture.js';
|
|
15
|
+
import { registerRecall } from './hooks/recall.js';
|
|
16
|
+
import { MemoryRunner } from './pipeline/runner.js';
|
|
17
|
+
import { RebuildController } from './pipeline/rebuild.js';
|
|
18
|
+
import { RuminateController } from './pipeline/ruminate.js';
|
|
19
|
+
import { registerMemoryRpc, PLUGIN_VERSION } from './stats.js';
|
|
20
|
+
import { registerLiveSettings } from './settings.js';
|
|
21
|
+
import { NoopEmbeddingService } from './store/embedding.js';
|
|
22
|
+
import { EmbeddingManager, EmbeddingSourceStore, makeLocalServiceFactory, resolveInitialEmbedding, } from './store/embedding-source.js';
|
|
23
|
+
import { ModelDownloadQueue } from './store/download-queue.js';
|
|
24
|
+
import { PINNED_TRANSFORMERS_VERSION, RuntimeInstaller } from './store/runtime-installer.js';
|
|
25
|
+
import { ensureDir } from './util/io.js';
|
|
26
|
+
import { L0Store } from './store/l0.js';
|
|
27
|
+
import { L1Store } from './store/l1.js';
|
|
28
|
+
import { PersonaStore } from './store/persona.js';
|
|
29
|
+
import { MemoryDb } from './store/sqlite.js';
|
|
30
|
+
import { SceneStore } from './store/scenes.js';
|
|
31
|
+
import { SessionModeStore } from './store/session-modes.js';
|
|
32
|
+
import { StateStore } from './store/state.js';
|
|
33
|
+
import { registerMemoryTools } from './tools/index.js';
|
|
34
|
+
import { errDetail, withFileLog } from './util/filelog.js';
|
|
35
|
+
import { buildRouteChain, resolveModelRoute, invalidateEffortCache } from './llm.js';
|
|
36
|
+
import { effectiveCfg } from './pipeline/runner.js';
|
|
37
|
+
import { pendingPathFor } from './store/pending.js';
|
|
38
|
+
import { initTokenCost, resetTokenCost } from './token-cost.js';
|
|
39
|
+
export const name = 'dsh-memory-plugin';
|
|
40
|
+
/** 硬依赖:蒸馏要用 llm,工具注册要用 tools,召回注入要用 systemPrompt。 */
|
|
41
|
+
export const inject = ['llm', 'tools', 'systemPrompt'];
|
|
42
|
+
/**
|
|
43
|
+
* 插件配置 schema。导出名必须是 `Config`——cordis 运行时只读 plugin.Config
|
|
44
|
+
* (Standard Schema 接口)做校验与默认值填充;导出 `schema` 会被静默忽略,
|
|
45
|
+
* 导致 config 里嵌套对象为 undefined、apply 抛错、fiber FAILED 拖垮宿主启动。
|
|
46
|
+
*/
|
|
47
|
+
export const Config = memorySchema;
|
|
48
|
+
export async function apply(ctx, config) {
|
|
49
|
+
let logger = {
|
|
50
|
+
debug: (m) => ctx.logger.debug(m),
|
|
51
|
+
info: (m) => ctx.logger.info(m),
|
|
52
|
+
warn: (m) => ctx.logger.warn(m),
|
|
53
|
+
error: (m) => ctx.logger.error(m),
|
|
54
|
+
};
|
|
55
|
+
const dataDir = resolveDataDir(config);
|
|
56
|
+
// dsh 宿主无持久化日志,镜像 info+ 到数据目录 memory.log 供蒸馏问题诊断
|
|
57
|
+
const fileLogger = withFileLog(dataDir, logger);
|
|
58
|
+
logger = fileLogger;
|
|
59
|
+
// 供应商拓扑变化(增删/改配置)→ 思考档位能力缓存失效,下次调用重新探询
|
|
60
|
+
ctx.on('llm/adapters-updated', () => invalidateEffortCache());
|
|
61
|
+
// 存储初始化失败只降级(禁用捕获/蒸馏),绝不拖垮宿主——
|
|
62
|
+
// 记忆是增强能力,数据目录不可写时 dsh 本体必须照常启动。
|
|
63
|
+
let storageOk = true;
|
|
64
|
+
try {
|
|
65
|
+
await ensureDir(dataDir);
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
storageOk = false;
|
|
69
|
+
logger.error(`[memory] 数据目录不可写,记忆功能停用: ${dataDir} (${err instanceof Error ? err.message : String(err)})`);
|
|
70
|
+
}
|
|
71
|
+
// ── 记忆模式运行时开关(官方 settings 服务,live 生效;缺失时恒开) ──
|
|
72
|
+
const live = registerLiveSettings(ctx, logger);
|
|
73
|
+
/** 插件停机标志:置位后不再发起后台 embeddings 调用(dispose 序最先设置)。 */
|
|
74
|
+
let disposed = false;
|
|
75
|
+
// ── 会话档位存储(sessionId → auto/chat/work/off;默认档 = config.family) ──
|
|
76
|
+
const modes = new SessionModeStore(dataDir, config.family, logger);
|
|
77
|
+
if (storageOk) {
|
|
78
|
+
try {
|
|
79
|
+
await modes.init();
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
logger.warn(`[memory] 会话档位载入失败(降级为默认档内存态): ${err instanceof Error ? err.message : String(err)}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// ── 嵌入源三态(远程/本地/关闭)——状态文件优先于静态配置 ──
|
|
86
|
+
const sourceStore = new EmbeddingSourceStore(dataDir, logger);
|
|
87
|
+
const installer = new RuntimeInstaller(dataDir, PINNED_TRANSFORMERS_VERSION, { logger });
|
|
88
|
+
const downloader = new ModelDownloadQueue(dataDir, {
|
|
89
|
+
mirror: config.embedding.mirror,
|
|
90
|
+
logger,
|
|
91
|
+
proxy: config.embedding.proxy,
|
|
92
|
+
});
|
|
93
|
+
const makeLocalService = makeLocalServiceFactory(installer, downloader, logger, config.embedding.maxInputChars);
|
|
94
|
+
let initial = { svc: new NoopEmbeddingService(), dims: 0 };
|
|
95
|
+
/** 管理器引用:启动重嵌链/backfill 闭包在运行期解引用(声明早于创建避免 TDZ)。 */
|
|
96
|
+
// eslint-disable-next-line prefer-const -- 声明早于创建,后续单点赋值(TS 不允许 const 无初值)
|
|
97
|
+
let embedManagerRef;
|
|
98
|
+
if (storageOk) {
|
|
99
|
+
try {
|
|
100
|
+
await sourceStore.init();
|
|
101
|
+
initial = await resolveInitialEmbedding(config, sourceStore, downloader, makeLocalService, logger);
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
logger.warn(`[memory] 嵌入源初始解析失败(降级纯 FTS): ${errDetail(err)}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/** meta 写入永远用当前目标(杜绝启动期闭包里的陈旧 providerInfo 腐蚀 embedding_meta)。 */
|
|
108
|
+
const currentProviderInfo = () => embedManagerRef?.currentProviderInfo() ?? initial.providerInfo;
|
|
109
|
+
// ── 检索引擎(memory.db)与向量服务 ──
|
|
110
|
+
const embed = initial.svc;
|
|
111
|
+
const db = new MemoryDb(path.join(dataDir, 'memory.db'), initial.dims, logger);
|
|
112
|
+
initTokenCost(db, config.tokenCost.retentionDays);
|
|
113
|
+
// 插件卸载时关闭连接(WAL 落盘),注册一次即可
|
|
114
|
+
ctx.effect(() => () => db.close());
|
|
115
|
+
let dbInit = { needsReindex: false };
|
|
116
|
+
if (storageOk) {
|
|
117
|
+
try {
|
|
118
|
+
dbInit = db.init(initial.providerInfo);
|
|
119
|
+
if (db.isDegraded()) {
|
|
120
|
+
storageOk = false;
|
|
121
|
+
logger.error('[memory] 检索库初始化失败(schema/FTS 不可用),记忆功能停用');
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
storageOk = false;
|
|
126
|
+
logger.error(`[memory] 检索库打开失败,记忆功能停用: ${err instanceof Error ? err.message : String(err)}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
const stores = {
|
|
130
|
+
l0: new L0Store(dataDir, db, embed, logger),
|
|
131
|
+
l1: new L1Store(dataDir, db, embed, config.recall.strategy, logger, config.recall.decayHalfLifeDays),
|
|
132
|
+
// L2/L3 分族隔离:各自目录与文件(scenes/chat|work、persona-chat|work.md)
|
|
133
|
+
scenes: {
|
|
134
|
+
chat: new SceneStore(dataDir, 'chat', logger),
|
|
135
|
+
work: new SceneStore(dataDir, 'work', logger),
|
|
136
|
+
},
|
|
137
|
+
persona: {
|
|
138
|
+
chat: new PersonaStore(dataDir, 'chat', logger),
|
|
139
|
+
work: new PersonaStore(dataDir, 'work', logger),
|
|
140
|
+
},
|
|
141
|
+
state: new StateStore(StateStore.pathFor(dataDir)),
|
|
142
|
+
// 图谱存储(MemoryDb 内自治:初始化失败仅图谱 no-op,不影响主链路)
|
|
143
|
+
graph: db.graphStore,
|
|
144
|
+
};
|
|
145
|
+
if (storageOk) {
|
|
146
|
+
try {
|
|
147
|
+
await Promise.all([
|
|
148
|
+
stores.l0.init(),
|
|
149
|
+
stores.l1.init(),
|
|
150
|
+
stores.scenes.chat.init(),
|
|
151
|
+
stores.scenes.work.init(),
|
|
152
|
+
stores.persona.chat.init(),
|
|
153
|
+
stores.persona.work.init(),
|
|
154
|
+
]);
|
|
155
|
+
}
|
|
156
|
+
catch (err) {
|
|
157
|
+
storageOk = false;
|
|
158
|
+
logger.error(`[memory] 存储初始化失败,记忆功能停用: ${err instanceof Error ? err.message : String(err)}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// 检索能力与策略说明(能力位由降级结果决定)
|
|
162
|
+
const caps = db.getCapabilities();
|
|
163
|
+
if (storageOk && config.recall.strategy !== 'keyword' && !caps.vectorSearch) {
|
|
164
|
+
logger.warn(`[memory] 检索策略 ${config.recall.strategy} 需要向量能力(embedding 未启用/不可用),本次运行按 keyword(FTS5 BM25)检索`);
|
|
165
|
+
}
|
|
166
|
+
logger.info(`[memory] 数据目录: ${dataDir}(v${PLUGIN_VERSION},默认档=${config.family},检索: FTS=${caps.ftsSearch} 向量=${caps.vectorSearch}${storageOk ? '' : ',存储不可用已停用捕获/蒸馏'})`);
|
|
167
|
+
// 蒸馏模型路由:启动期解析一次并记录(路由错误是最难事后排查的问题之一);
|
|
168
|
+
// 用运行时调参视图解析——用户已用 UI 覆盖蒸馏模型时,日志反映实际路由
|
|
169
|
+
try {
|
|
170
|
+
const cfgView = effectiveCfg(config, live);
|
|
171
|
+
const route = await resolveModelRoute(ctx, cfgView);
|
|
172
|
+
// 链长度按同一解析口径计(相同条目会被去重),排障时无需 --dump-config 即可确认回退链生效
|
|
173
|
+
const chain = buildRouteChain(route, cfgView.llm.fallbacks, cfgView.llm.reasoningEffort);
|
|
174
|
+
logger.info(`[memory] 蒸馏模型路由: ${route.provider}/${route.model}${chain.length > 1 ? `(+${chain.length - 1} 回退)` : ''}`);
|
|
175
|
+
}
|
|
176
|
+
catch (err) {
|
|
177
|
+
logger.warn(`[memory] 蒸馏模型路由解析失败: ${errDetail(err)}`);
|
|
178
|
+
}
|
|
179
|
+
// embedding 配置变化 → 后台全量重嵌入(不阻塞启动)。
|
|
180
|
+
// meta 标记条件 = 失败为 0 且缺失复查为 0(服务未就绪的空转不算成功)。
|
|
181
|
+
if (storageOk && initial.providerInfo && !db.isDegraded()) {
|
|
182
|
+
const info = initial.providerInfo;
|
|
183
|
+
if (dbInit.needsReindex) {
|
|
184
|
+
if (caps.vectorSearch) {
|
|
185
|
+
void (async () => {
|
|
186
|
+
try {
|
|
187
|
+
if (disposed || embedManagerRef?.isBusy())
|
|
188
|
+
return;
|
|
189
|
+
// 增量:配置变化路径向量表已被 drop,全部记录均"缺失",等效全量;
|
|
190
|
+
// 活切换发起时让路(swap 会 drop 表,并发跑只是白烧嵌入调用)
|
|
191
|
+
const shouldCancel = () => disposed || !!embedManagerRef?.isBusy();
|
|
192
|
+
const r1 = await stores.l1.reindex({ shouldCancel });
|
|
193
|
+
const r0 = await stores.l0.reindex({ shouldCancel });
|
|
194
|
+
// missing 复查:嵌入服务未就绪时 reindex 短路 0/0/0——那是"没跑"不是"成功",
|
|
195
|
+
// 不得标记 meta(否则启动链不再补,补齐被无限推迟到 backfill)
|
|
196
|
+
const missingAfter = db.countL1VecMissing(db.getVecSkipSet('l1')) + db.countL0VecMissing(db.getVecSkipSet('l0'));
|
|
197
|
+
if (r1.failed === 0 && r0.failed === 0 && missingAfter === 0) {
|
|
198
|
+
db.markEmbeddingSynced(currentProviderInfo() ?? info);
|
|
199
|
+
const skipNote = r1.skipped + r0.skipped > 0 ? `,跳过不可嵌入 ${r1.skipped + r0.skipped} 条` : '';
|
|
200
|
+
logger.info(`[memory] 向量重建完成:L1 ${r1.written} 条,L0 ${r0.written} 条${skipNote}(原因:${dbInit.reason ?? '配置变化'})`);
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
logger.warn(`[memory] 向量重建未完成(L1 失败 ${r1.failed},L0 失败 ${r0.failed},剩缺 ${Math.max(missingAfter, 0)} 条),下次启动/补齐周期重试`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
catch (err) {
|
|
207
|
+
logger.warn(`[memory] 向量重建失败: ${err instanceof Error ? err.message : String(err)}`);
|
|
208
|
+
}
|
|
209
|
+
})();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
// meta 已匹配或空库(initSchema 已写入),幂等重写一次
|
|
214
|
+
db.markEmbeddingSynced(currentProviderInfo() ?? info);
|
|
215
|
+
}
|
|
216
|
+
// 周期性向量补齐:嵌入失败的批次/漏网记录自动补上(30 分钟一次)。
|
|
217
|
+
// 判据 = 排除 skip 集后的缺失数(增量语义):缺 1 条只补 1 条,
|
|
218
|
+
// 零向量记录进 skip 集后不再触发——否则补齐永不收敛、每 30 分钟全量重嵌。
|
|
219
|
+
// 注册不依赖启动期 caps/providerInfo(off 起步后活切回旧源也要能补),
|
|
220
|
+
// 执行期动态查当前目标与能力位。
|
|
221
|
+
{
|
|
222
|
+
const backfill = () => {
|
|
223
|
+
void (async () => {
|
|
224
|
+
try {
|
|
225
|
+
if (disposed)
|
|
226
|
+
return;
|
|
227
|
+
const infoNow = currentProviderInfo();
|
|
228
|
+
const capsNow = db.getCapabilities();
|
|
229
|
+
if (!infoNow || !capsNow.vectorSearch)
|
|
230
|
+
return;
|
|
231
|
+
// 活切换链(安装/下载/重嵌)进行中让路——增量 reindex 天然幂等,
|
|
232
|
+
// 但并发跑只是白烧一次嵌入调用
|
|
233
|
+
if (embedManagerRef?.isBusy())
|
|
234
|
+
return;
|
|
235
|
+
const l1Missing = db.countL1VecMissing(db.getVecSkipSet('l1')) > 0;
|
|
236
|
+
const l0Missing = db.countL0VecMissing(db.getVecSkipSet('l0')) > 0;
|
|
237
|
+
if (!l1Missing && !l0Missing)
|
|
238
|
+
return;
|
|
239
|
+
const shouldCancel = () => disposed || !!embedManagerRef?.isBusy();
|
|
240
|
+
const r1 = await stores.l1.reindex({ shouldCancel });
|
|
241
|
+
const r0 = await stores.l0.reindex({ shouldCancel });
|
|
242
|
+
const missingAfter = db.countL1VecMissing(db.getVecSkipSet('l1')) + db.countL0VecMissing(db.getVecSkipSet('l0'));
|
|
243
|
+
if (r1.failed === 0 && r0.failed === 0 && missingAfter === 0) {
|
|
244
|
+
db.markEmbeddingSynced(infoNow);
|
|
245
|
+
logger.info(`[memory] 向量补齐完成:L1 ${r1.written} 条,L0 ${r0.written} 条(跳过不可嵌入 ${r1.skipped + r0.skipped} 条)`);
|
|
246
|
+
}
|
|
247
|
+
else if (r1.failed > 0 || r0.failed > 0) {
|
|
248
|
+
logger.warn(`[memory] 向量补齐未完成(L1 失败 ${r1.failed},L0 失败 ${r0.failed}),下个周期重试`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
catch (err) {
|
|
252
|
+
logger.warn(`[memory] 向量补齐失败: ${err instanceof Error ? err.message : String(err)}`);
|
|
253
|
+
}
|
|
254
|
+
})();
|
|
255
|
+
};
|
|
256
|
+
ctx.effect(() => {
|
|
257
|
+
const timer = setInterval(backfill, 30 * 60_000);
|
|
258
|
+
const first = setTimeout(backfill, 60_000);
|
|
259
|
+
return () => {
|
|
260
|
+
clearInterval(timer);
|
|
261
|
+
clearTimeout(first);
|
|
262
|
+
};
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
// 嵌入管理器(活切换/下载/运行时安装;存储降级时不建——RPC 走 supported=false)
|
|
267
|
+
const embedManager = storageOk && !db.isDegraded()
|
|
268
|
+
? new EmbeddingManager({
|
|
269
|
+
dataDir,
|
|
270
|
+
cfg: config,
|
|
271
|
+
db,
|
|
272
|
+
l0: stores.l0,
|
|
273
|
+
l1: stores.l1,
|
|
274
|
+
sourceStore,
|
|
275
|
+
installer,
|
|
276
|
+
downloader,
|
|
277
|
+
initial,
|
|
278
|
+
logger,
|
|
279
|
+
makeLocal: makeLocalService,
|
|
280
|
+
})
|
|
281
|
+
: undefined;
|
|
282
|
+
embedManagerRef = embedManager;
|
|
283
|
+
const runner = new MemoryRunner(ctx, config, stores, logger, live, modes);
|
|
284
|
+
await runner.init();
|
|
285
|
+
// 档位切换同步(ADR-0003):切走按捕获档位落袋 / 切 off 挂起 / 切回清挂起
|
|
286
|
+
modes.setModeChangeHandler((sessionId, oldMode, newMode) => runner.onModeChange(sessionId, oldMode, newMode));
|
|
287
|
+
// 闲置兜底:静默达标会话的未蒸馏切片自动落袋(idleSeconds=0 关闭)
|
|
288
|
+
runner.startIdleTimer();
|
|
289
|
+
// 图谱周期补投影(cfg.graph.enabled 门控;30 分钟一次低速收敛)
|
|
290
|
+
runner.startGraphBackfillTimer();
|
|
291
|
+
// 重建控制器(存储降级时不建——RPC 端点走 supported=false 分支)
|
|
292
|
+
const rebuild = storageOk && !db.isDegraded()
|
|
293
|
+
? new RebuildController(ctx, config, stores, db, runner, logger, live)
|
|
294
|
+
: undefined;
|
|
295
|
+
// 反刍控制器(与重建共用数据,但更轻量;存储降级时不建)
|
|
296
|
+
const ruminateFile = storageOk ? pendingPathFor(resolveDataDir(config)) : '';
|
|
297
|
+
const ruminate = storageOk && !db.isDegraded() && ruminateFile
|
|
298
|
+
? new RuminateController(ctx, config, runner, stores, logger, live, ruminateFile)
|
|
299
|
+
: undefined;
|
|
300
|
+
let flushL0;
|
|
301
|
+
if (storageOk) {
|
|
302
|
+
flushL0 = registerCapture(ctx, config, runner, stores.l0, logger, live, modes);
|
|
303
|
+
}
|
|
304
|
+
const recall = registerRecall(ctx, config, stores, logger, live, modes, dataDir);
|
|
305
|
+
runner.setAfterRun(recall.invalidateProfile);
|
|
306
|
+
registerMemoryTools(ctx, config, stores, logger, modes, live, ruminate);
|
|
307
|
+
registerMemoryRpc(ctx, config, stores, logger, {
|
|
308
|
+
degraded: () => !storageOk || db.isDegraded(),
|
|
309
|
+
pending: () => runner.pendingCount,
|
|
310
|
+
}, live, modes, dataDir, rebuild, embedManager,
|
|
311
|
+
// 悬浮卡信息区数据源(session-stats 热路径端点;全部内存读 + 索引 COUNT,零文件 I/O)
|
|
312
|
+
{
|
|
313
|
+
recallStats: (sid) => recall.stats(sid),
|
|
314
|
+
memoryOccupancy: (sid) => recall.occupancy(sid),
|
|
315
|
+
profileEstimate: (sid) => recall.estimateProfileTokens(sid),
|
|
316
|
+
recallEstimate: (sid) => recall.estimateRecallTokens(sid),
|
|
317
|
+
runnerView: (sid, mode) => runner.sessionView(sid, mode),
|
|
318
|
+
l0Count: (sid) => stores.l0.countBySession(sid),
|
|
319
|
+
capabilities: () => db.getCapabilities(),
|
|
320
|
+
}, ruminate);
|
|
321
|
+
// bench 控制服务(config.benchControl 门控,默认关):仅基准/调试部署注册,
|
|
322
|
+
// 供同进程的 bench-runner lifecycle 赛道触发 rebuild / 设置会话档位
|
|
323
|
+
// (宿主侧 RPC 无 call(),见 bench-control.ts)
|
|
324
|
+
if (config.benchControl && rebuild) {
|
|
325
|
+
const disposeBench = registerBenchControl(ctx, rebuild, modes, logger);
|
|
326
|
+
ctx.effect(() => () => disposeBench());
|
|
327
|
+
}
|
|
328
|
+
logger.info(`[memory] L0~L3 分层蒸馏记忆插件就绪(L1 记忆 ${storageOk ? stores.l1.size : 0} 条 | 捕获=${storageOk && config.capture.enabled} | 蒸馏=${storageOk && config.extract.enabled} | 召回=${config.recall.enabled})`);
|
|
329
|
+
// 停机顺序:置停机标志(后台 embeddings 不再发起)→ 停蒸馏取新任务 →
|
|
330
|
+
// 冲刷 L0 串行链(排队消息先落盘)→ 关库。cordis disposer 为 LIFO 逐个 await,
|
|
331
|
+
// 本 effect 晚注册故先执行;apply 中途失败时由上方早注册的 db.close 兜底(双关安全)。
|
|
332
|
+
ctx.effect(() => () => {
|
|
333
|
+
disposed = true;
|
|
334
|
+
runner.stop();
|
|
335
|
+
embedManager?.dispose();
|
|
336
|
+
downloader.dispose();
|
|
337
|
+
return (async () => {
|
|
338
|
+
await flushL0?.();
|
|
339
|
+
db.close();
|
|
340
|
+
db.graphStore.close();
|
|
341
|
+
resetTokenCost();
|
|
342
|
+
})();
|
|
343
|
+
});
|
|
344
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 蒸馏用量追踪(进程内累计计数器,常开——纯内存累加,无 IO):
|
|
3
|
+
* callLLM 按层(l1-extract/l1-dedup/l2/l3)累计调用数、输入字符、输出/思考 token。
|
|
4
|
+
*
|
|
5
|
+
* 用途:基准的「记忆开销」记账——L1/L2/L3 蒸馏消耗摊到每条捕获消息。
|
|
6
|
+
* 暴露面:bench 控制服务 getDistillUsage()(config.benchControl 门控)。
|
|
7
|
+
*
|
|
8
|
+
* 输入侧说明:dsh llm 流的 usage 块只携带 output/reasoning token,输入 token
|
|
9
|
+
* 拿不到——按仓库既有口径记输入字符(中文 1 字 ≈ 1 token 保守折算)。
|
|
10
|
+
*/
|
|
11
|
+
import type { DistillLayer } from './contract.js';
|
|
12
|
+
export type { DistillLayer } from './contract.js';
|
|
13
|
+
export interface DistillLayerUsage {
|
|
14
|
+
calls: number;
|
|
15
|
+
failures: number;
|
|
16
|
+
inputChars: number;
|
|
17
|
+
outputTokens: number;
|
|
18
|
+
reasoningTokens: number;
|
|
19
|
+
}
|
|
20
|
+
export interface DistillUsageSnapshot {
|
|
21
|
+
layers: Record<string, DistillLayerUsage>;
|
|
22
|
+
}
|
|
23
|
+
/** 单次调用用量累计(callLLM 出口调用;inputChars 为实际发送的用户 prompt 字符)。 */
|
|
24
|
+
export declare function recordDistillCall(layer: DistillLayer, inputChars: number, outputTokens: number, reasoningTokens: number, failed: boolean): void;
|
|
25
|
+
/** 深拷贝快照(消费方拿到后不受后续累计影响)。 */
|
|
26
|
+
export declare function snapshotDistillUsage(): DistillUsageSnapshot;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 蒸馏用量追踪(进程内累计计数器,常开——纯内存累加,无 IO):
|
|
3
|
+
* callLLM 按层(l1-extract/l1-dedup/l2/l3)累计调用数、输入字符、输出/思考 token。
|
|
4
|
+
*
|
|
5
|
+
* 用途:基准的「记忆开销」记账——L1/L2/L3 蒸馏消耗摊到每条捕获消息。
|
|
6
|
+
* 暴露面:bench 控制服务 getDistillUsage()(config.benchControl 门控)。
|
|
7
|
+
*
|
|
8
|
+
* 输入侧说明:dsh llm 流的 usage 块只携带 output/reasoning token,输入 token
|
|
9
|
+
* 拿不到——按仓库既有口径记输入字符(中文 1 字 ≈ 1 token 保守折算)。
|
|
10
|
+
*/
|
|
11
|
+
const counters = new Map();
|
|
12
|
+
function bucketOf(layer) {
|
|
13
|
+
let b = counters.get(layer);
|
|
14
|
+
if (!b) {
|
|
15
|
+
b = { calls: 0, failures: 0, inputChars: 0, outputTokens: 0, reasoningTokens: 0 };
|
|
16
|
+
counters.set(layer, b);
|
|
17
|
+
}
|
|
18
|
+
return b;
|
|
19
|
+
}
|
|
20
|
+
/** 单次调用用量累计(callLLM 出口调用;inputChars 为实际发送的用户 prompt 字符)。 */
|
|
21
|
+
export function recordDistillCall(layer, inputChars, outputTokens, reasoningTokens, failed) {
|
|
22
|
+
const b = bucketOf(layer);
|
|
23
|
+
b.calls++;
|
|
24
|
+
if (failed)
|
|
25
|
+
b.failures++;
|
|
26
|
+
b.inputChars += Math.max(0, Math.round(inputChars));
|
|
27
|
+
b.outputTokens += Math.max(0, Math.round(outputTokens));
|
|
28
|
+
b.reasoningTokens += Math.max(0, Math.round(reasoningTokens));
|
|
29
|
+
}
|
|
30
|
+
/** 深拷贝快照(消费方拿到后不受后续累计影响)。 */
|
|
31
|
+
export function snapshotDistillUsage() {
|
|
32
|
+
const layers = {};
|
|
33
|
+
for (const [layer, b] of counters) {
|
|
34
|
+
layers[layer] = { ...b };
|
|
35
|
+
}
|
|
36
|
+
return { layers };
|
|
37
|
+
}
|
package/dist/llm.d.ts
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM 桥:用 DSH 自身的 llm 服务跑蒸馏调用。
|
|
3
|
+
*
|
|
4
|
+
* 职责:蒸馏路由链(全局回退链 + 按层层链)、思考档位能力决策(跨供应商 effort
|
|
5
|
+
* 兼容)、分层输出预算(思考档 ×4 防线)、direct 原生 HTTP 通道、流式调用与
|
|
6
|
+
* 失败诊断、容错 JSON 解析。回退语义见 ADR-0004/0005。
|
|
7
|
+
*/
|
|
8
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
9
|
+
import type { MemoryConfig } from './config.js';
|
|
10
|
+
import type { MemoryLogger } from './types.js';
|
|
11
|
+
import { type DistillLayer } from './llm-usage.js';
|
|
12
|
+
export interface LlmCallOptions {
|
|
13
|
+
system: string;
|
|
14
|
+
user: string;
|
|
15
|
+
/** 蒸馏层标签(用量记账用;四层调用点都应传)。 */
|
|
16
|
+
layer?: DistillLayer;
|
|
17
|
+
maxTokens?: number;
|
|
18
|
+
temperature?: number;
|
|
19
|
+
signal?: AbortSignal;
|
|
20
|
+
/** 诊断日志(缺失时静默,不影响调用) */
|
|
21
|
+
logger?: MemoryLogger;
|
|
22
|
+
}
|
|
23
|
+
/** L1 抽取(大输入块的 JSON 记忆数组输出)。 */
|
|
24
|
+
export declare const LAYER_MAX_TOKENS_EXTRACT = 16000;
|
|
25
|
+
/** L1 去重(合并决策数组,输出比抽取短)。 */
|
|
26
|
+
export declare const LAYER_MAX_TOKENS_DEDUP = 8000;
|
|
27
|
+
/** L2 场景整合(完整场景 Markdown 文件输出,输出最重的层)。 */
|
|
28
|
+
export declare const LAYER_MAX_TOKENS_L2 = 32000;
|
|
29
|
+
/** L3 画像(完整 persona 文档)。 */
|
|
30
|
+
export declare const LAYER_MAX_TOKENS_L3 = 16000;
|
|
31
|
+
/** 图谱投影(节点/边提案 JSON;单批 ≤8 条记录,输出比 L1 抽取短)。 */
|
|
32
|
+
export declare const LAYER_MAX_TOKENS_GRAPH = 8000;
|
|
33
|
+
/** 分层输出预算键(契约单一事实源在 src/contract.ts)。 */
|
|
34
|
+
import type { DistillBudgetLayer, LayerRouteKey, StaticFallbackEntry } from './contract.js';
|
|
35
|
+
export type { DistillBudgetLayer } from './contract.js';
|
|
36
|
+
/** 各层内置默认预算(设置页"0 = 跟随默认"的默认值来源)。 */
|
|
37
|
+
export declare const LAYER_DEFAULT_BUDGETS: Record<DistillBudgetLayer, number>;
|
|
38
|
+
/** resolveLayerTokens/layerEffortTrigger 需要的最小 cfg 视图(决策表用窄对象即可构造)。 */
|
|
39
|
+
export interface LayerRouteCfgView {
|
|
40
|
+
llm: {
|
|
41
|
+
reasoningEffort: string;
|
|
42
|
+
primaryEffort?: string;
|
|
43
|
+
layerRoutes?: Partial<Record<LayerRouteKey, StaticFallbackEntry[]>>;
|
|
44
|
+
layerChainsRuntime?: Partial<Record<LayerRouteKey, StaticFallbackEntry[]>>;
|
|
45
|
+
budgets?: Partial<Record<DistillBudgetLayer, number>>;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* 解析某蒸馏层的生效输出预算:运行时覆盖(cfg.llm.budgets,0/缺省 = 跟随)
|
|
50
|
+
* → 内置默认 → 思考档放大(high/xhigh/max ×4,reasoning 计入输出预算的历史事故
|
|
51
|
+
* 防线)。放大触发档位跟层走:层链头档位候选 > 全局主路由档位候选;
|
|
52
|
+
* graph 层无层链(不落 l1 路由),恒走全局候选。
|
|
53
|
+
*/
|
|
54
|
+
export declare function resolveLayerTokens(cfg: LayerRouteCfgView, layer: DistillBudgetLayer): number;
|
|
55
|
+
/**
|
|
56
|
+
* 高思考档集合(输出预算 ×4 的档位):阶段侧 layerMaxTokens 与 callLLM 的
|
|
57
|
+
* 自动档防线共用同一张表——勿再在别处抄写该列表。
|
|
58
|
+
*/
|
|
59
|
+
export declare const HIGH_EFFORT_TIERS: readonly ["high", "xhigh", "max"];
|
|
60
|
+
/**
|
|
61
|
+
* 思考档预算放大:reasoning 计入输出预算(v4-flash 事故:high 思考可吃光全部
|
|
62
|
+
* 预算致正文 0 字符)——effort 为 high/xhigh/max 时分层预算 ×4。
|
|
63
|
+
*/
|
|
64
|
+
export declare function layerMaxTokens(base: number, reasoningEffort: string): number;
|
|
65
|
+
/** 解析蒸馏用的 provider/model:配置优先,其次当前默认选择。 */
|
|
66
|
+
export declare function resolveModelRoute(ctx: Context, cfg: MemoryConfig): Promise<{
|
|
67
|
+
provider: string;
|
|
68
|
+
model: string;
|
|
69
|
+
}>;
|
|
70
|
+
/** 回退链条目(配置形态;reasoningEffort 为该路由的档位覆盖,'' = 跟随全局)。 */
|
|
71
|
+
export interface FallbackRouteEntry {
|
|
72
|
+
provider: string;
|
|
73
|
+
model: string;
|
|
74
|
+
reasoningEffort?: string;
|
|
75
|
+
}
|
|
76
|
+
/** 链上单条路由(档位候选已解析:条目非空 > 全局静态;发送前仍过能力钳制)。 */
|
|
77
|
+
export interface DistillRoute {
|
|
78
|
+
provider: string;
|
|
79
|
+
model: string;
|
|
80
|
+
effort: string;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* 组装蒸馏路由链(纯决策,决策表缝):主路由在前、回退条目按配置顺序在后。
|
|
84
|
+
* provider/model 缺失的条目剔除;与主路由或先前条目完全相同(provider+model)的
|
|
85
|
+
* 条目跳过——注定失败的重复尝试不值得占位。每条路由携带生效档位候选:
|
|
86
|
+
* 主路由可带显式档位(运行时统一链注入 primaryEffort),条目档位非空覆盖全局。
|
|
87
|
+
*/
|
|
88
|
+
export declare function buildRouteChain(primary: {
|
|
89
|
+
provider: string;
|
|
90
|
+
model: string;
|
|
91
|
+
effort?: string;
|
|
92
|
+
}, fallbacks: FallbackRouteEntry[] | undefined, globalEffort: string): DistillRoute[];
|
|
93
|
+
/**
|
|
94
|
+
* DistillLayer(调用点五键)→ 路由层键(三键):l1-extract/l1-dedup 同属 l1;
|
|
95
|
+
* graph 无层路由键——返回 null,调用侧按"该层无层链"回全局解析,绝不落入 l1
|
|
96
|
+
* (图谱投影误用 L1 抽取链是路由配置错误,静默错路由最难排查)。
|
|
97
|
+
*/
|
|
98
|
+
export declare function layerKeyFor(layer: DistillLayer): LayerRouteKey | null;
|
|
99
|
+
/** 该层的预算放大触发档位:层链头档位候选 > 全局主路由档位候选(primaryEffort > 静态全局)。
|
|
100
|
+
* key=null(graph 层无层链)→ 恒走全局候选。 */
|
|
101
|
+
export declare function layerEffortTrigger(cfg: LayerRouteCfgView, key: LayerRouteKey | null): string;
|
|
102
|
+
/**
|
|
103
|
+
* 解析某次蒸馏调用的实际路由链(callLLM 入口):有层标签且该层配了层链 → 层链
|
|
104
|
+
* 完整替换(buildRouteChain 复用:头行在前、条目去重、档位三级候选);否则现行
|
|
105
|
+
* 全局解析。layer 缺省(bench/测试缝)= 全局解析。
|
|
106
|
+
*/
|
|
107
|
+
export declare function resolveLayerRoutes(ctx: Context, cfg: MemoryConfig, layer?: DistillLayer): Promise<DistillRoute[]>;
|
|
108
|
+
/** 层链解析的同步半边(llm-providers 视图与 resolveLayerRoutes 共用一条真值路径):
|
|
109
|
+
* 该层配了有效层链 → 完整链;null = 该层跟随全局解析。 */
|
|
110
|
+
export declare function layerChainOrNull(cfg: LayerRouteCfgView, key: LayerRouteKey): DistillRoute[] | null;
|
|
111
|
+
export interface ModelEffortInfo {
|
|
112
|
+
/** 模型可设置的思考档位 id(适配器声明;空 = 未声明/不可设置) */
|
|
113
|
+
efforts: string[];
|
|
114
|
+
/** 适配器配置的默认档位(省略 effort 时的请求值) */
|
|
115
|
+
defaultEffort?: string;
|
|
116
|
+
}
|
|
117
|
+
/** 清空能力缓存(llm/adapters-updated 时调用:供应商增删/改配置后重新探询)。 */
|
|
118
|
+
export declare function invalidateEffortCache(): void;
|
|
119
|
+
/** 探询某模型的思考档位能力;失败返回 null(调用方保持旧发送行为,不改判)。 */
|
|
120
|
+
export declare function resolveModelEfforts(ctx: Context, provider: string, model: string): Promise<ModelEffortInfo | null>;
|
|
121
|
+
/**
|
|
122
|
+
* 探询某模型的上下文窗口容量(adapter 声明的 provider-owned capacity)。
|
|
123
|
+
* 与 effortCache 同源同失效策略;仅用于占用指示器的分母展示——分母必须与官方环
|
|
124
|
+
* 同源(模型声明值),禁止 client 自估。
|
|
125
|
+
*/
|
|
126
|
+
export declare function resolveModelContextWindow(ctx: Context, provider: string, model: string): Promise<number | null>;
|
|
127
|
+
export type EffortDecisionReason = 'supported' | 'auto-default' | 'auto-high' | 'alias-none' | 'unsupported' | 'no-efforts' | 'no-capability';
|
|
128
|
+
export interface EffortDecision {
|
|
129
|
+
/** 实际发送的档位;'' = 不发送(跟随模型默认) */
|
|
130
|
+
effort: string;
|
|
131
|
+
reason: EffortDecisionReason;
|
|
132
|
+
}
|
|
133
|
+
/** 纯决策:配置档位 + 模型能力 → 实际发送值(callLLM 与 settings-get 共用)。 */
|
|
134
|
+
export declare function decideSendableEffort(cap: ModelEffortInfo | null, cfgEffort: string): EffortDecision;
|
|
135
|
+
/** 探询 + 决策 + 一次性告警(不支持/未声明时提示降级,不刷屏)。 */
|
|
136
|
+
export declare function planDistillEffort(ctx: Context, provider: string, model: string, cfgEffort: string, logger?: MemoryLogger): Promise<EffortDecision>;
|
|
137
|
+
/**
|
|
138
|
+
* 一次完整蒸馏调用(带回退链,ADR-0004):按路由链(主路由 + llm.fallbacks)逐条
|
|
139
|
+
* 尝试,返回首个成功路由的输出。失败(error/aborted finish、网络异常、空输出)
|
|
140
|
+
* 降级下一条;调用方主动取消(signal 已中止)原样上抛不降级;全部失败抛最后一个
|
|
141
|
+
* 错误,由调用方兜底(runner 的按会话指数退避接管重试节奏)。
|
|
142
|
+
*
|
|
143
|
+
* llm.mode='direct' 时压缩首选走插件原生 HTTP(callDirect)——与付费 API 解耦;
|
|
144
|
+
* direct 失败(含未配置 baseURL/model)自动回退下方宿主路由链作兜底安全网。
|
|
145
|
+
*
|
|
146
|
+
* 文本只从 block-end(协议保证携带组装完成的整块)取;text-delta 仅在
|
|
147
|
+
* 适配器异常地没有发 block-end 时兜底。两者都累计会把输出翻倍。
|
|
148
|
+
*/
|
|
149
|
+
export declare function callLLM(ctx: Context, cfg: MemoryConfig, opts: LlmCallOptions): Promise<string>;
|
|
150
|
+
/** 带诊断日志的 parseJson:解析失败时记录原始输出摘录(模型输出异常排查的关键信息)。 */
|
|
151
|
+
export declare function parseJsonLogged<T>(raw: string, what: string, logger?: MemoryLogger): T;
|
|
152
|
+
/** 容错地解析 LLM 输出的 JSON(剥掉可能的 ```json 围栏)。 */
|
|
153
|
+
export declare function parseJson<T>(raw: string): T;
|