@tencent-connect/dsh-qqbot 0.1.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/.orange-ci.yml +68 -0
- package/LICENSE +21 -0
- package/README.md +142 -0
- package/README_EN.md +142 -0
- package/cordis.patch.yml +32 -0
- package/cordis.yml +69 -0
- package/dist/commands/help.d.ts +11 -0
- package/dist/commands/help.d.ts.map +1 -0
- package/dist/commands/help.js +21 -0
- package/dist/commands/help.js.map +1 -0
- package/dist/commands/index.d.ts +13 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +27 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/misc.d.ts +12 -0
- package/dist/commands/misc.d.ts.map +1 -0
- package/dist/commands/misc.js +30 -0
- package/dist/commands/misc.js.map +1 -0
- package/dist/commands/model.d.ts +7 -0
- package/dist/commands/model.d.ts.map +1 -0
- package/dist/commands/model.js +58 -0
- package/dist/commands/model.js.map +1 -0
- package/dist/commands/session.d.ts +10 -0
- package/dist/commands/session.d.ts.map +1 -0
- package/dist/commands/session.js +26 -0
- package/dist/commands/session.js.map +1 -0
- package/dist/commands/status.d.ts +8 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +24 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/types.d.ts +12 -0
- package/dist/commands/types.d.ts.map +1 -0
- package/dist/commands/types.js +2 -0
- package/dist/commands/types.js.map +1 -0
- package/dist/config.d.ts +50 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +33 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +154 -0
- package/dist/index.js.map +1 -0
- package/dist/model/index.d.ts +10 -0
- package/dist/model/index.d.ts.map +1 -0
- package/dist/model/index.js +9 -0
- package/dist/model/index.js.map +1 -0
- package/dist/model/model-resolver.d.ts +82 -0
- package/dist/model/model-resolver.d.ts.map +1 -0
- package/dist/model/model-resolver.js +142 -0
- package/dist/model/model-resolver.js.map +1 -0
- package/dist/model/prefs-store.d.ts +24 -0
- package/dist/model/prefs-store.d.ts.map +1 -0
- package/dist/model/prefs-store.js +94 -0
- package/dist/model/prefs-store.js.map +1 -0
- package/dist/model/settings-reader.d.ts +19 -0
- package/dist/model/settings-reader.d.ts.map +1 -0
- package/dist/model/settings-reader.js +76 -0
- package/dist/model/settings-reader.js.map +1 -0
- package/dist/model/types.d.ts +15 -0
- package/dist/model/types.d.ts.map +1 -0
- package/dist/model/types.js +5 -0
- package/dist/model/types.js.map +1 -0
- package/dist/session/idle-evictor.d.ts +18 -0
- package/dist/session/idle-evictor.d.ts.map +1 -0
- package/dist/session/idle-evictor.js +31 -0
- package/dist/session/idle-evictor.js.map +1 -0
- package/dist/session/index.d.ts +9 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/index.js +8 -0
- package/dist/session/index.js.map +1 -0
- package/dist/session/session-manager.d.ts +44 -0
- package/dist/session/session-manager.d.ts.map +1 -0
- package/dist/session/session-manager.js +341 -0
- package/dist/session/session-manager.js.map +1 -0
- package/dist/session/types.d.ts +124 -0
- package/dist/session/types.d.ts.map +1 -0
- package/dist/session/types.js +2 -0
- package/dist/session/types.js.map +1 -0
- package/dist/setup.d.ts +24 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +135 -0
- package/dist/setup.js.map +1 -0
- package/dist/shared/index.d.ts +7 -0
- package/dist/shared/index.d.ts.map +1 -0
- package/dist/shared/index.js +7 -0
- package/dist/shared/index.js.map +1 -0
- package/dist/shared/scope.d.ts +14 -0
- package/dist/shared/scope.d.ts.map +1 -0
- package/dist/shared/scope.js +12 -0
- package/dist/shared/scope.js.map +1 -0
- package/dist/shared/send-helper.d.ts +11 -0
- package/dist/shared/send-helper.d.ts.map +1 -0
- package/dist/shared/send-helper.js +13 -0
- package/dist/shared/send-helper.js.map +1 -0
- package/dist/shared/utils.d.ts +19 -0
- package/dist/shared/utils.d.ts.map +1 -0
- package/dist/shared/utils.js +66 -0
- package/dist/shared/utils.js.map +1 -0
- package/dist/transport/chunker.d.ts +14 -0
- package/dist/transport/chunker.d.ts.map +1 -0
- package/dist/transport/chunker.js +78 -0
- package/dist/transport/chunker.js.map +1 -0
- package/dist/transport/inbound.d.ts +8 -0
- package/dist/transport/inbound.d.ts.map +1 -0
- package/dist/transport/inbound.js +223 -0
- package/dist/transport/inbound.js.map +1 -0
- package/dist/transport/index.d.ts +10 -0
- package/dist/transport/index.d.ts.map +1 -0
- package/dist/transport/index.js +10 -0
- package/dist/transport/index.js.map +1 -0
- package/dist/transport/outbound-buffer.d.ts +32 -0
- package/dist/transport/outbound-buffer.d.ts.map +1 -0
- package/dist/transport/outbound-buffer.js +65 -0
- package/dist/transport/outbound-buffer.js.map +1 -0
- package/dist/transport/outbound.d.ts +26 -0
- package/dist/transport/outbound.d.ts.map +1 -0
- package/dist/transport/outbound.js +92 -0
- package/dist/transport/outbound.js.map +1 -0
- package/dist/types.d.ts +34 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/install.sh +108 -0
- package/package.json +75 -0
- package/pnpm-workspace.yaml +16 -0
- package/scripts/prepare-test-pkg.js +23 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { getScopePeer, sendMarkdownChunked } from '../shared/index.js';
|
|
2
|
+
export function modelCommand({ manager, config }) {
|
|
3
|
+
return {
|
|
4
|
+
name: 'bot-model',
|
|
5
|
+
description: '查看或切换模型(用法: /bot-model [provider/model])',
|
|
6
|
+
handler: async (cmdCtx) => {
|
|
7
|
+
const { scope, peerId } = getScopePeer(cmdCtx);
|
|
8
|
+
const args = (cmdCtx.command?.raw ?? '').trim();
|
|
9
|
+
// 无参数:显示当前模型 + 可用模型列表(可点击)
|
|
10
|
+
if (!args) {
|
|
11
|
+
const current = manager.getEffectiveModel(scope, peerId);
|
|
12
|
+
const models = manager.listAvailableModels();
|
|
13
|
+
// 当前模型展示:优先用别名(name),找不到别名时回退到 provider/model id
|
|
14
|
+
let currentDisplay = '宿主默认配置';
|
|
15
|
+
if (current) {
|
|
16
|
+
const matched = models.find((m) => m.provider === current.provider && m.id === current.model);
|
|
17
|
+
currentDisplay = matched?.name ?? `${current.provider}/${current.model}`;
|
|
18
|
+
}
|
|
19
|
+
const lines = [
|
|
20
|
+
'### 🤖 模型配置',
|
|
21
|
+
'',
|
|
22
|
+
`**当前模型:** ${currentDisplay}`,
|
|
23
|
+
];
|
|
24
|
+
if (models.length > 0) {
|
|
25
|
+
lines.push('', '**可用模型(点击切换):**');
|
|
26
|
+
for (const m of models) {
|
|
27
|
+
const modelPath = `${m.provider}/${m.id}`;
|
|
28
|
+
const displayName = m.name ? `${m.name}` : modelPath;
|
|
29
|
+
lines.push(`<qqbot-cmd-input text="/bot-model ${modelPath}" show="/bot-model ${displayName}"/>`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
lines.push('', '手动指定: `/bot-model provider/model`');
|
|
33
|
+
await sendMarkdownChunked(cmdCtx, lines.join('\n'), config.textChunkLimit);
|
|
34
|
+
return { kind: 'noop' };
|
|
35
|
+
}
|
|
36
|
+
// 解析 provider/model 格式
|
|
37
|
+
let provider;
|
|
38
|
+
let model;
|
|
39
|
+
if (args.includes('/')) {
|
|
40
|
+
const parts = args.split('/');
|
|
41
|
+
provider = parts[0] ?? '';
|
|
42
|
+
model = parts.slice(1).join('/');
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
// 仅指定 model 名,provider 从当前路由继承
|
|
46
|
+
const current = manager.getEffectiveModel(scope, peerId);
|
|
47
|
+
provider = current?.provider ?? 'deepseek-official';
|
|
48
|
+
model = args;
|
|
49
|
+
}
|
|
50
|
+
if (!provider || !model) {
|
|
51
|
+
return '用法: /bot-model provider/model\n示例: /bot-model deepseek-official/deepseek-chat';
|
|
52
|
+
}
|
|
53
|
+
await manager.setModelOverride(scope, peerId, { provider, model });
|
|
54
|
+
return `✅ 模型已切换: ${provider}/${model}\n立即生效,对话上下文保留。`;
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/commands/model.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 会话相关命令:/bot-reset /bot-clear /bot-new
|
|
3
|
+
*/
|
|
4
|
+
import type { SlashCommand } from '@tencent-connect/qqbot-nodejs';
|
|
5
|
+
import type { CommandDeps } from './types.js';
|
|
6
|
+
/** /bot-reset /bot-clear — 重置当前会话 */
|
|
7
|
+
export declare function resetCommand({ manager }: CommandDeps): SlashCommand;
|
|
8
|
+
/** /bot-new — 开始新会话 */
|
|
9
|
+
export declare function newCommand({ manager }: CommandDeps): SlashCommand;
|
|
10
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/commands/session.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG9C,qCAAqC;AACrC,wBAAgB,YAAY,CAAC,EAAE,OAAO,EAAE,EAAE,WAAW,GAAG,YAAY,CAUnE;AAED,uBAAuB;AACvB,wBAAgB,UAAU,CAAC,EAAE,OAAO,EAAE,EAAE,WAAW,GAAG,YAAY,CAUjE"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { getScopePeer } from '../shared/index.js';
|
|
2
|
+
/** /bot-reset /bot-clear — 重置当前会话 */
|
|
3
|
+
export function resetCommand({ manager }) {
|
|
4
|
+
return {
|
|
5
|
+
name: ['bot-reset', 'bot-clear'],
|
|
6
|
+
description: '重置当前会话(清除上下文)',
|
|
7
|
+
handler: (cmdCtx) => {
|
|
8
|
+
const { scope, peerId } = getScopePeer(cmdCtx);
|
|
9
|
+
void manager.remove(scope, peerId);
|
|
10
|
+
return '会话已重置 ✓';
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
/** /bot-new — 开始新会话 */
|
|
15
|
+
export function newCommand({ manager }) {
|
|
16
|
+
return {
|
|
17
|
+
name: 'bot-new',
|
|
18
|
+
description: '开始新会话(清空上下文)',
|
|
19
|
+
handler: (cmdCtx) => {
|
|
20
|
+
const { scope, peerId } = getScopePeer(cmdCtx);
|
|
21
|
+
void manager.remove(scope, peerId);
|
|
22
|
+
return '已开启新会话 ✓';
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/commands/session.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,qCAAqC;AACrC,MAAM,UAAU,YAAY,CAAC,EAAE,OAAO,EAAe;IACnD,OAAO;QACL,IAAI,EAAE,CAAC,WAAW,EAAE,WAAW,CAAC;QAChC,WAAW,EAAE,eAAe;QAC5B,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE;YAClB,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;YAC/C,KAAK,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;YACnC,OAAO,SAAS,CAAC;QACnB,CAAC;KACF,CAAC;AACJ,CAAC;AAED,uBAAuB;AACvB,MAAM,UAAU,UAAU,CAAC,EAAE,OAAO,EAAe;IACjD,OAAO;QACL,IAAI,EAAE,SAAS;QACf,WAAW,EAAE,cAAc;QAC3B,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE;YAClB,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;YAC/C,KAAK,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;YACnC,OAAO,UAAU,CAAC;QACpB,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 状态命令:/bot-status
|
|
3
|
+
*/
|
|
4
|
+
import type { SlashCommand } from '@tencent-connect/qqbot-nodejs';
|
|
5
|
+
import type { CommandDeps } from './types.js';
|
|
6
|
+
/** /bot-status — 查看当前会话状态 */
|
|
7
|
+
export declare function statusCommand({ manager }: CommandDeps): SlashCommand;
|
|
8
|
+
//# sourceMappingURL=status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG9C,6BAA6B;AAC7B,wBAAgB,aAAa,CAAC,EAAE,OAAO,EAAE,EAAE,WAAW,GAAG,YAAY,CAmBpE"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { getScopePeer, formatRelativeTime } from '../shared/index.js';
|
|
2
|
+
/** /bot-status — 查看当前会话状态 */
|
|
3
|
+
export function statusCommand({ manager }) {
|
|
4
|
+
return {
|
|
5
|
+
name: 'bot-status',
|
|
6
|
+
description: '查看当前会话状态',
|
|
7
|
+
handler: (cmdCtx) => {
|
|
8
|
+
const { scope, peerId } = getScopePeer(cmdCtx);
|
|
9
|
+
const status = manager.getStatus(scope, peerId);
|
|
10
|
+
if (!status.active)
|
|
11
|
+
return '当前无活跃会话';
|
|
12
|
+
const modelInfo = status.model ? `${status.provider}/${status.model}` : '宿主默认';
|
|
13
|
+
return [
|
|
14
|
+
'📊 会话状态',
|
|
15
|
+
`会话: ${status.sessionId ? status.sessionId.slice(0, 8) : '—'}`,
|
|
16
|
+
`模型: ${modelInfo}`,
|
|
17
|
+
`Preset: ${status.preset ?? '无'}`,
|
|
18
|
+
`消息数: ${status.messageCount ?? 0}`,
|
|
19
|
+
`最后活动: ${formatRelativeTime(status.lastActivity)}`,
|
|
20
|
+
].join('\n');
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAEtE,6BAA6B;AAC7B,MAAM,UAAU,aAAa,CAAC,EAAE,OAAO,EAAe;IACpD,OAAO;QACL,IAAI,EAAE,YAAY;QAClB,WAAW,EAAE,UAAU;QACvB,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE;YAClB,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;YAC/C,MAAM,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;YAChD,IAAI,CAAC,MAAM,CAAC,MAAM;gBAAE,OAAO,SAAS,CAAC;YACrC,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;YAC/E,OAAO;gBACL,SAAS;gBACT,OAAO,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE;gBAC9D,OAAO,SAAS,EAAE;gBAClB,WAAW,MAAM,CAAC,MAAM,IAAI,GAAG,EAAE;gBACjC,QAAQ,MAAM,CAAC,YAAY,IAAI,CAAC,EAAE;gBAClC,SAAS,kBAAkB,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE;aACnD,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACf,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 命令依赖类型
|
|
3
|
+
*
|
|
4
|
+
* 每个命令工厂函数接收 CommandDeps,由 commands/index.ts 统一注入。
|
|
5
|
+
*/
|
|
6
|
+
import type { SessionManager } from '../session/index.js';
|
|
7
|
+
import type { ImQQBotConfig } from '../config.js';
|
|
8
|
+
export interface CommandDeps {
|
|
9
|
+
manager: SessionManager;
|
|
10
|
+
config: ImQQBotConfig;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/commands/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,cAAc,CAAC;IACxB,MAAM,EAAE,aAAa,CAAC;CACvB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/commands/types.ts"],"names":[],"mappings":""}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-im-qqbot 插件配置 Schema
|
|
3
|
+
*/
|
|
4
|
+
import Schema from '@deepseek-ai/schemastery';
|
|
5
|
+
export interface AccessControlConfig {
|
|
6
|
+
/** C2C 访问模式 */
|
|
7
|
+
c2cMode: 'open' | 'allowlist' | 'disabled';
|
|
8
|
+
/** C2C 白名单(user openid) */
|
|
9
|
+
c2cAllow: string[];
|
|
10
|
+
/** 群聊访问模式 */
|
|
11
|
+
groupMode: 'open' | 'allowlist' | 'disabled';
|
|
12
|
+
/** 群聊白名单(group openid) */
|
|
13
|
+
groupAllow: string[];
|
|
14
|
+
}
|
|
15
|
+
export interface ImQQBotConfig {
|
|
16
|
+
/** QQ Bot AppID */
|
|
17
|
+
appId: string;
|
|
18
|
+
/** QQ Bot AppSecret */
|
|
19
|
+
appSecret: string;
|
|
20
|
+
/** dsh LLM 提供商名称 */
|
|
21
|
+
provider?: string;
|
|
22
|
+
/** 模型名称 */
|
|
23
|
+
model?: string;
|
|
24
|
+
/** Agent preset id */
|
|
25
|
+
preset?: string;
|
|
26
|
+
/** Agent 工作目录 */
|
|
27
|
+
cwd: string;
|
|
28
|
+
/** 是否启用群消息 @mention 门控 */
|
|
29
|
+
requireMention: boolean;
|
|
30
|
+
/** 群聊额外 system prompt */
|
|
31
|
+
groupPrompt?: string;
|
|
32
|
+
/** 私聊额外 system prompt */
|
|
33
|
+
directPrompt?: string;
|
|
34
|
+
/** 单条消息最大长度(QQ 限制约 5000 字符) */
|
|
35
|
+
textChunkLimit: number;
|
|
36
|
+
/** 每会话最大闲置时长(ms),超时自动回收 */
|
|
37
|
+
sessionIdleTimeout: number;
|
|
38
|
+
/** 并发队列最大长度 */
|
|
39
|
+
maxQueue: number;
|
|
40
|
+
/** 处理超时(ms),超时中断当前 LLM 调用 */
|
|
41
|
+
processingTimeoutMs: number;
|
|
42
|
+
/** 群历史缓冲条数 */
|
|
43
|
+
historyLimit: number;
|
|
44
|
+
/** 访问控制 */
|
|
45
|
+
access: AccessControlConfig;
|
|
46
|
+
/** 调试模式 */
|
|
47
|
+
debug: boolean;
|
|
48
|
+
}
|
|
49
|
+
export declare const ConfigSchema: Schema<ImQQBotConfig>;
|
|
50
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,MAAM,MAAM,0BAA0B,CAAC;AAE9C,MAAM,WAAW,mBAAmB;IAClC,eAAe;IACf,OAAO,EAAE,MAAM,GAAG,WAAW,GAAG,UAAU,CAAC;IAC3C,2BAA2B;IAC3B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,aAAa;IACb,SAAS,EAAE,MAAM,GAAG,WAAW,GAAG,UAAU,CAAC;IAC7C,0BAA0B;IAC1B,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,mBAAmB;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,uBAAuB;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,oBAAoB;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sBAAsB;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iBAAiB;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,0BAA0B;IAC1B,cAAc,EAAE,OAAO,CAAC;IACxB,yBAAyB;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yBAAyB;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,+BAA+B;IAC/B,cAAc,EAAE,MAAM,CAAC;IACvB,2BAA2B;IAC3B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,6BAA6B;IAC7B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,cAAc;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW;IACX,MAAM,EAAE,mBAAmB,CAAC;IAC5B,WAAW;IACX,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,aAAa,CA2B7C,CAAC"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-im-qqbot 插件配置 Schema
|
|
3
|
+
*/
|
|
4
|
+
import Schema from '@deepseek-ai/schemastery';
|
|
5
|
+
export const ConfigSchema = Schema.object({
|
|
6
|
+
appId: Schema.string().default('').description('QQ Bot AppID'),
|
|
7
|
+
appSecret: Schema.string().default('').description('QQ Bot AppSecret'),
|
|
8
|
+
provider: Schema.string().description('LLM provider name'),
|
|
9
|
+
model: Schema.string().description('Model name'),
|
|
10
|
+
preset: Schema.string().description('Agent preset id'),
|
|
11
|
+
cwd: Schema.string().default('/').description('Agent working directory'),
|
|
12
|
+
requireMention: Schema.boolean().default(true).description('群聊是否需要@bot触发'),
|
|
13
|
+
groupPrompt: Schema.string().description('群聊额外system prompt'),
|
|
14
|
+
directPrompt: Schema.string().description('私聊额外system prompt'),
|
|
15
|
+
textChunkLimit: Schema.number().default(4500).description('单条消息最大字符数'),
|
|
16
|
+
sessionIdleTimeout: Schema.number().default(30 * 60 * 1000).description('会话闲置超时(ms)'),
|
|
17
|
+
maxQueue: Schema.number().default(20).description('并发队列最大长度'),
|
|
18
|
+
processingTimeoutMs: Schema.number().default(120000).description('处理超时(ms)'),
|
|
19
|
+
historyLimit: Schema.number().default(10).description('群历史缓冲条数'),
|
|
20
|
+
access: Schema.object({
|
|
21
|
+
c2cMode: Schema.union(['open', 'allowlist', 'disabled']).default('open').description('C2C访问模式'),
|
|
22
|
+
c2cAllow: Schema.array(Schema.string()).default([]).description('C2C白名单'),
|
|
23
|
+
groupMode: Schema.union(['open', 'allowlist', 'disabled']).default('open').description('群聊访问模式'),
|
|
24
|
+
groupAllow: Schema.array(Schema.string()).default([]).description('群聊白名单'),
|
|
25
|
+
}).default({
|
|
26
|
+
c2cMode: 'open',
|
|
27
|
+
c2cAllow: [],
|
|
28
|
+
groupMode: 'open',
|
|
29
|
+
groupAllow: [],
|
|
30
|
+
}).description('访问控制'),
|
|
31
|
+
debug: Schema.boolean().default(false),
|
|
32
|
+
});
|
|
33
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,MAAM,MAAM,0BAA0B,CAAC;AAgD9C,MAAM,CAAC,MAAM,YAAY,GAA0B,MAAM,CAAC,MAAM,CAAC;IAC/D,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,cAAc,CAAC;IAC9D,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,kBAAkB,CAAC;IACtE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,mBAAmB,CAAC;IAC1D,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,YAAY,CAAC;IAChD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,iBAAiB,CAAC;IACtD,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,WAAW,CAAC,yBAAyB,CAAC;IACxE,cAAc,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,WAAW,CAAC,cAAc,CAAC;IAC1E,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,mBAAmB,CAAC;IAC7D,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,mBAAmB,CAAC;IAC9D,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,WAAW,CAAC,WAAW,CAAC;IACtE,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,WAAW,CAAC,YAAY,CAAC;IACrF,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,UAAU,CAAC;IAC7D,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,UAAU,CAAC;IAC5E,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,SAAS,CAAC;IAChE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC;QACpB,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,SAAS,CAAC;QAC/F,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,QAAQ,CAAC;QACzE,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,QAAQ,CAAC;QAChG,UAAU,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC;KAC3E,CAAC,CAAC,OAAO,CAAC;QACT,OAAO,EAAE,MAAM;QACf,QAAQ,EAAE,EAAE;QACZ,SAAS,EAAE,MAAM;QACjB,UAAU,EAAE,EAAE;KACf,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;CACvC,CAAC,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-im-qqbot — QQ Bot IM channel plugin for deepseek-harness
|
|
3
|
+
*
|
|
4
|
+
* Cordis 插件入口。将 QQ 消息平台作为 dsh 的前端协议驱动。
|
|
5
|
+
* 复用 @tencent-connect/qqbot-nodejs SDK 全套中间件链。
|
|
6
|
+
*/
|
|
7
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
8
|
+
import { type ImQQBotConfig } from './config.js';
|
|
9
|
+
export declare const name = "im-qqbot";
|
|
10
|
+
export declare const inject: string[];
|
|
11
|
+
export declare const Config: import("@deepseek-ai/schemastery").default<ImQQBotConfig>;
|
|
12
|
+
export type { ImQQBotConfig } from './config.js';
|
|
13
|
+
export declare function apply(ctx: Context, config: ImQQBotConfig): Promise<void>;
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAmBnD,OAAO,EAAgB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAS/D,eAAO,MAAM,IAAI,aAAa,CAAC;AAC/B,eAAO,MAAM,MAAM,UAAa,CAAC;AACjC,eAAO,MAAM,MAAM,2DAAe,CAAC;AAEnC,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGjD,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAmC9E"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { QQBot, errorHandler, messageFilter, accessPolicy, mentionGate, contentSanitizer, rateLimiter, slashCommand, concurrencyGuard, typingIndicator, quoteRef, historyBuffer, envelopeFormatter, MemoryHistoryStore, } from '@tencent-connect/qqbot-nodejs';
|
|
2
|
+
import { ConfigSchema } from './config.js';
|
|
3
|
+
import { SessionManager } from './session/index.js';
|
|
4
|
+
import { handleInbound, createOutboundHandler } from './transport/index.js';
|
|
5
|
+
import { buildCommandList } from './commands/index.js';
|
|
6
|
+
import { getProfileDir, resolveEnv, buildUserAgent } from './shared/index.js';
|
|
7
|
+
import { runQrSetup, persistCredentialsToProfile } from './setup.js';
|
|
8
|
+
// ── Cordis 插件元数据 ──
|
|
9
|
+
export const name = 'im-qqbot';
|
|
10
|
+
export const inject = ['agents'];
|
|
11
|
+
export const Config = ConfigSchema;
|
|
12
|
+
// ── 插件主体 ──
|
|
13
|
+
export async function apply(ctx, config) {
|
|
14
|
+
const agents = ctx.agents;
|
|
15
|
+
const logger = ctx.logger ?? console;
|
|
16
|
+
console.log('[im-qqbot] apply() called');
|
|
17
|
+
let appId = resolveEnv(config.appId, 'QQBOT_APPID');
|
|
18
|
+
let appSecret = resolveEnv(config.appSecret, 'QQBOT_SECRET');
|
|
19
|
+
// ── 凭据缺失时唤起扫码绑定 ──
|
|
20
|
+
if (!appId || !appSecret) {
|
|
21
|
+
logger.info('凭据未配置,尝试扫码绑定...');
|
|
22
|
+
const credentials = await runQrSetup();
|
|
23
|
+
if (!credentials) {
|
|
24
|
+
logger.error('无法获取 QQ Bot 凭据,插件未启动');
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
// 持久化到 profile 配置
|
|
28
|
+
persistCredentialsToProfile(credentials, getProfileDir() ?? undefined, logger);
|
|
29
|
+
// 写入环境变量供热更新后的下次 apply 读取
|
|
30
|
+
process.env.QQBOT_APPID = credentials.appId;
|
|
31
|
+
process.env.QQBOT_SECRET = credentials.appSecret;
|
|
32
|
+
// 写入 cordis.patch.yml 会触发 dsh 热更新,自动重新加载本插件
|
|
33
|
+
// 此处直接返回,避免与热更新产生竞态
|
|
34
|
+
logger.info('配置已保存,等待热更新重新加载...');
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const resolvedConfig = { ...config, appId, appSecret };
|
|
38
|
+
await bootstrap(ctx, agents, resolvedConfig, logger);
|
|
39
|
+
}
|
|
40
|
+
async function bootstrap(ctx, agents, config, logger) {
|
|
41
|
+
const manager = new SessionManager(ctx, agents, config, logger);
|
|
42
|
+
// ── 初始化 QQ Bot SDK ──
|
|
43
|
+
const userAgent = buildUserAgent();
|
|
44
|
+
const bot = new QQBot({
|
|
45
|
+
appId: config.appId,
|
|
46
|
+
appSecret: config.appSecret,
|
|
47
|
+
transport: 'websocket',
|
|
48
|
+
userAgent,
|
|
49
|
+
logger,
|
|
50
|
+
});
|
|
51
|
+
logger.info(`QQBot SDK initialized (UA: ${userAgent})`);
|
|
52
|
+
// ══════════════════════════════════════════════════════════════
|
|
53
|
+
// SDK 中间件链(洋葱模型,按执行顺序编排)
|
|
54
|
+
// ══════════════════════════════════════════════════════════════
|
|
55
|
+
// 1. 错误兜底(最外层洋葱皮)
|
|
56
|
+
bot.use(errorHandler());
|
|
57
|
+
// 2. 消息过滤:bot 回声 + 消息去重
|
|
58
|
+
bot.use(messageFilter({ skipSelfEcho: false }));
|
|
59
|
+
// 3. 访问控制(白名单/开放/禁用)
|
|
60
|
+
bot.use(accessPolicy({
|
|
61
|
+
c2c: {
|
|
62
|
+
mode: config.access.c2cMode,
|
|
63
|
+
allow: config.access.c2cAllow.length > 0 ? config.access.c2cAllow : ['*'],
|
|
64
|
+
},
|
|
65
|
+
group: {
|
|
66
|
+
mode: config.access.groupMode,
|
|
67
|
+
allow: config.access.groupAllow.length > 0 ? config.access.groupAllow : ['*'],
|
|
68
|
+
},
|
|
69
|
+
onBlock: (_mCtx, reason) => {
|
|
70
|
+
if (config.debug) {
|
|
71
|
+
logger.debug(`Access blocked: ${reason}`);
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
}));
|
|
75
|
+
// 4. 群历史缓冲 — 放在门控之前,确保所有消息(含未 @bot)都计入上下文
|
|
76
|
+
const historyStore = new MemoryHistoryStore();
|
|
77
|
+
bot.use(historyBuffer({
|
|
78
|
+
limit: config.historyLimit,
|
|
79
|
+
store: historyStore,
|
|
80
|
+
recordOnSkip: true,
|
|
81
|
+
}));
|
|
82
|
+
// 5. 群聊 @bot 门控
|
|
83
|
+
bot.use(mentionGate({
|
|
84
|
+
requireMentionInGroup: config.requireMention,
|
|
85
|
+
}));
|
|
86
|
+
// 6. 内容清洗(去 @marker、表情标签、多余空白)
|
|
87
|
+
bot.use(contentSanitizer({
|
|
88
|
+
parseFaceTags: true,
|
|
89
|
+
}));
|
|
90
|
+
// 7. 三层限流(sender / group / global)
|
|
91
|
+
bot.use(rateLimiter());
|
|
92
|
+
// 8. 斜杠命令(在 concurrencyGuard 之前,命令匹配后不排队直接响应)
|
|
93
|
+
const slash = slashCommand({
|
|
94
|
+
autoHelp: true,
|
|
95
|
+
commands: buildCommandList({ manager, config }),
|
|
96
|
+
});
|
|
97
|
+
bot.use(slash.middleware);
|
|
98
|
+
// 9. 并发串行 + 消息合并(同 peer 排队,避免 session 冲突)
|
|
99
|
+
bot.use(concurrencyGuard({
|
|
100
|
+
strategy: 'merge',
|
|
101
|
+
maxQueue: config.maxQueue,
|
|
102
|
+
maxProcessingMs: config.processingTimeoutMs,
|
|
103
|
+
urgentPredicate: (mCtx) => {
|
|
104
|
+
return (mCtx.message.content ?? '').trim() === '/bot-stop';
|
|
105
|
+
},
|
|
106
|
+
}));
|
|
107
|
+
// 10. C2C 输入状态指示
|
|
108
|
+
bot.use(typingIndicator());
|
|
109
|
+
// 11. 引用消息解析(记录 + 解析被引用原文)
|
|
110
|
+
bot.use(quoteRef({
|
|
111
|
+
maxSize: 500,
|
|
112
|
+
preferMsgElements: true,
|
|
113
|
+
}));
|
|
114
|
+
// 12. 上下文组装(将 history + quote + sender 组成 envelope)
|
|
115
|
+
bot.use(envelopeFormatter({
|
|
116
|
+
historyLimit: config.historyLimit,
|
|
117
|
+
includeQuote: true,
|
|
118
|
+
includeSender: true,
|
|
119
|
+
}));
|
|
120
|
+
// ══════════════════════════════════════════════════════════════
|
|
121
|
+
// 最终处理:经过中间件链后的消息交给 dsh agent
|
|
122
|
+
// ══════════════════════════════════════════════════════════════
|
|
123
|
+
bot.on('message', async (mCtx) => {
|
|
124
|
+
const msg = mCtx.message;
|
|
125
|
+
if (config.debug) {
|
|
126
|
+
logger.debug(`← message (post-middleware): ${JSON.stringify(msg, null, 2).slice(0, 500)}`);
|
|
127
|
+
}
|
|
128
|
+
await handleInbound(msg, manager, config, logger, mCtx.state);
|
|
129
|
+
});
|
|
130
|
+
// ── 出站 ──
|
|
131
|
+
const outboundHandler = createOutboundHandler(manager, bot, config, logger);
|
|
132
|
+
ctx
|
|
133
|
+
.on('session/event', outboundHandler);
|
|
134
|
+
bot.on('error', (err) => {
|
|
135
|
+
logger.error(`bot error: ${err instanceof Error ? err.message : String(err)}`);
|
|
136
|
+
});
|
|
137
|
+
bot.on('ready', () => {
|
|
138
|
+
console.log(`[im-qqbot] Bot ready! appId=${config.appId}`);
|
|
139
|
+
});
|
|
140
|
+
// ── 生命周期 ──
|
|
141
|
+
ctx
|
|
142
|
+
.effect(() => {
|
|
143
|
+
logger.info(`Starting bot (appId=${config.appId})`);
|
|
144
|
+
bot.start().catch((err) => {
|
|
145
|
+
logger.error(`Bot start failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
146
|
+
});
|
|
147
|
+
return async () => {
|
|
148
|
+
logger.info('Shutting down');
|
|
149
|
+
await manager.disposeAll();
|
|
150
|
+
bot.stop();
|
|
151
|
+
};
|
|
152
|
+
}, 'im-qqbot.lifecycle');
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 模型路由层
|
|
3
|
+
*
|
|
4
|
+
* 统一的模型发现、路由解析、per-peer 偏好管理。
|
|
5
|
+
*/
|
|
6
|
+
export { ModelResolver } from './model-resolver.js';
|
|
7
|
+
export { PrefsStore } from './prefs-store.js';
|
|
8
|
+
export { SettingsReader } from './settings-reader.js';
|
|
9
|
+
export type { ModelRoute, ModelEntry } from './types.js';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/model/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/model/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ModelResolver — 统一的模型发现与路由解析
|
|
3
|
+
*
|
|
4
|
+
* 职责:
|
|
5
|
+
* 1. 解析当前生效的默认模型路由
|
|
6
|
+
* 2. 列出可用 providers 和模型
|
|
7
|
+
* 3. 管理 per-peer 的模型偏好(委托 PrefsStore)
|
|
8
|
+
*
|
|
9
|
+
* 优先级(从高到低):
|
|
10
|
+
* per-peer 偏好(~/.dsh-qqbot/model-prefs.json)
|
|
11
|
+
* > config 显式指定(cordis.yml 的 provider/model)
|
|
12
|
+
* > settings.yaml 的 agent-default-model(只读,作为默认兜底)
|
|
13
|
+
* > 宿主 agentDefaultModel 服务
|
|
14
|
+
*/
|
|
15
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
16
|
+
import type { ImQQBotConfig } from '../config.js';
|
|
17
|
+
import type { Logger } from '../types.js';
|
|
18
|
+
import type { ModelRoute, ModelEntry } from './types.js';
|
|
19
|
+
export declare class ModelResolver {
|
|
20
|
+
private readonly ctx;
|
|
21
|
+
private readonly config;
|
|
22
|
+
private readonly logger?;
|
|
23
|
+
private readonly prefs;
|
|
24
|
+
private readonly settings;
|
|
25
|
+
constructor(ctx: Context, config: ImQQBotConfig, logger?: Logger | undefined);
|
|
26
|
+
/**
|
|
27
|
+
* 获取指定 sessionKey 的有效模型路由(create 用)
|
|
28
|
+
*
|
|
29
|
+
* 优先级:per-peer 偏好 > config 显式指定 > settings.yaml > 宿主服务
|
|
30
|
+
*/
|
|
31
|
+
getEffectiveRoute(sessionKey: string): ModelRoute | undefined;
|
|
32
|
+
/**
|
|
33
|
+
* 获取 resume 时覆盖 session 的模型路由(对齐 dsh-TUI 语义)
|
|
34
|
+
*
|
|
35
|
+
* 仅「用户显式切换的 per-peer 偏好」和「cordis.yml 显式配置」才覆盖
|
|
36
|
+
* session 自己的 requestHeader;否则返回 undefined,让 session 沿用
|
|
37
|
+
* 自己历史里记录的模型(session 记住模型,避免重启后模型漂移)。
|
|
38
|
+
*/
|
|
39
|
+
getResumeRoute(sessionKey: string): ModelRoute | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* 设置 per-peer 模型偏好并持久化到隔离文件
|
|
42
|
+
*/
|
|
43
|
+
setOverride(sessionKey: string, route: ModelRoute): void;
|
|
44
|
+
/**
|
|
45
|
+
* 清除 per-peer 模型偏好并持久化
|
|
46
|
+
*/
|
|
47
|
+
clearOverride(sessionKey: string): void;
|
|
48
|
+
/**
|
|
49
|
+
* 是否存在指定 session 的模型偏好
|
|
50
|
+
*/
|
|
51
|
+
hasOverride(sessionKey: string): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* 获取指定 sessionKey 的最新 sessionId(fork 后记录,重启恢复用)
|
|
54
|
+
*/
|
|
55
|
+
getSessionId(sessionKey: string): string | undefined;
|
|
56
|
+
/**
|
|
57
|
+
* 记录指定 sessionKey 的最新 sessionId(fork 后调用)并持久化
|
|
58
|
+
*/
|
|
59
|
+
setSessionId(sessionKey: string, sessionId: string): void;
|
|
60
|
+
/**
|
|
61
|
+
* 清除指定 sessionKey 的 sessionId 记录
|
|
62
|
+
*/
|
|
63
|
+
clearSessionId(sessionKey: string): void;
|
|
64
|
+
/**
|
|
65
|
+
* 解析默认模型路由(不含 per-peer 偏好)
|
|
66
|
+
*
|
|
67
|
+
* 优先级:config 显式指定 > settings.yaml(只读) > 宿主 agentDefaultModel
|
|
68
|
+
*/
|
|
69
|
+
resolveDefault(): ModelRoute | undefined;
|
|
70
|
+
/**
|
|
71
|
+
* 列出所有可用模型
|
|
72
|
+
*/
|
|
73
|
+
listModels(): ModelEntry[];
|
|
74
|
+
/**
|
|
75
|
+
* 列出可用 provider 名称
|
|
76
|
+
*/
|
|
77
|
+
listProviders(): string[];
|
|
78
|
+
private readFromHost;
|
|
79
|
+
/** 统一的 Cordis 服务访问 */
|
|
80
|
+
private getService;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=model-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-resolver.d.ts","sourceRoot":"","sources":["../../src/model/model-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAIzD,qBAAa,aAAa;IAKtB,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IAN1B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiB;gBAGvB,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,aAAa,EACrB,MAAM,CAAC,EAAE,MAAM,YAAA;IAQlC;;;;OAIG;IACH,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAI7D;;;;;;OAMG;IACH,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAW1D;;OAEG;IACH,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI;IAIxD;;OAEG;IACH,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAIvC;;OAEG;IACH,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAIxC;;OAEG;IACH,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIpD;;OAEG;IACH,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAIzD;;OAEG;IACH,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAIxC;;;;OAIG;IACH,cAAc,IAAI,UAAU,GAAG,SAAS;IAWxC;;OAEG;IACH,UAAU,IAAI,UAAU,EAAE;IAI1B;;OAEG;IACH,aAAa,IAAI,MAAM,EAAE;IAuBzB,OAAO,CAAC,YAAY;IAoBpB,sBAAsB;IACtB,OAAO,CAAC,UAAU;CAKnB"}
|