foliko 1.0.74 → 1.0.75
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/.agent/.shared/ui-ux-pro-max/data/charts.csv +26 -0
- package/.agent/.shared/ui-ux-pro-max/data/colors.csv +97 -0
- package/.agent/.shared/ui-ux-pro-max/data/icons.csv +101 -0
- package/.agent/.shared/ui-ux-pro-max/data/landing.csv +31 -0
- package/.agent/.shared/ui-ux-pro-max/data/products.csv +97 -0
- package/.agent/.shared/ui-ux-pro-max/data/prompts.csv +24 -0
- package/.agent/.shared/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/.agent/.shared/ui-ux-pro-max/data/styles.csv +59 -0
- package/.agent/.shared/ui-ux-pro-max/data/typography.csv +58 -0
- package/.agent/.shared/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/.agent/.shared/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/.agent/.shared/ui-ux-pro-max/data/web-interface.csv +31 -0
- package/.agent/.shared/ui-ux-pro-max/scripts/__pycache__/core.cpython-313.pyc +0 -0
- package/.agent/.shared/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-313.pyc +0 -0
- package/.agent/.shared/ui-ux-pro-max/scripts/core.py +258 -0
- package/.agent/.shared/ui-ux-pro-max/scripts/design_system.py +1067 -0
- package/.agent/.shared/ui-ux-pro-max/scripts/search.py +106 -0
- package/.agent/ARCHITECTURE.md +288 -0
- package/.agent/agents/ambient-agent.md +57 -0
- package/.agent/agents/debugger.md +55 -0
- package/.agent/agents/email-assistant.md +49 -0
- package/.agent/agents/file-manager.md +42 -0
- package/.agent/agents/python-developer.md +60 -0
- package/.agent/agents/scheduler.md +59 -0
- package/.agent/agents/web-developer.md +45 -0
- package/.agent/data/default.json +29 -0
- package/.agent/data/plugins-state.json +255 -0
- package/.agent/mcp_config.json +4 -0
- package/.agent/mcp_config_updated.json +12 -0
- package/.agent/plugins.json +5 -0
- package/.agent/rules/GEMINI.md +273 -0
- package/.agent/rules/allow-rule.md +77 -0
- package/.agent/rules/log-rule.md +83 -0
- package/.agent/rules/security-rule.md +93 -0
- package/.agent/scripts/auto_preview.py +148 -0
- package/.agent/scripts/checklist.py +217 -0
- package/.agent/scripts/session_manager.py +120 -0
- package/.agent/scripts/verify_all.py +327 -0
- package/.agent/skills/api-patterns/SKILL.md +81 -0
- package/.agent/skills/api-patterns/api-style.md +42 -0
- package/.agent/skills/api-patterns/auth.md +24 -0
- package/.agent/skills/api-patterns/documentation.md +26 -0
- package/.agent/skills/api-patterns/graphql.md +41 -0
- package/.agent/skills/api-patterns/rate-limiting.md +31 -0
- package/.agent/skills/api-patterns/response.md +37 -0
- package/.agent/skills/api-patterns/rest.md +40 -0
- package/.agent/skills/api-patterns/scripts/api_validator.py +211 -0
- package/.agent/skills/api-patterns/security-testing.md +122 -0
- package/.agent/skills/api-patterns/trpc.md +41 -0
- package/.agent/skills/api-patterns/versioning.md +22 -0
- package/.agent/skills/app-builder/SKILL.md +75 -0
- package/.agent/skills/app-builder/agent-coordination.md +71 -0
- package/.agent/skills/app-builder/feature-building.md +53 -0
- package/.agent/skills/app-builder/project-detection.md +34 -0
- package/.agent/skills/app-builder/scaffolding.md +118 -0
- package/.agent/skills/app-builder/tech-stack.md +40 -0
- package/.agent/skills/app-builder/templates/SKILL.md +39 -0
- package/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +76 -0
- package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +92 -0
- package/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +88 -0
- package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +88 -0
- package/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +83 -0
- package/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +90 -0
- package/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +90 -0
- package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +122 -0
- package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +122 -0
- package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +169 -0
- package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +134 -0
- package/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +83 -0
- package/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +119 -0
- package/.agent/skills/architecture/SKILL.md +55 -0
- package/.agent/skills/architecture/context-discovery.md +43 -0
- package/.agent/skills/architecture/examples.md +94 -0
- package/.agent/skills/architecture/pattern-selection.md +68 -0
- package/.agent/skills/architecture/patterns-reference.md +50 -0
- package/.agent/skills/architecture/trade-off-analysis.md +77 -0
- package/.agent/skills/clean-code/SKILL.md +201 -0
- package/.agent/skills/doc.md +177 -0
- package/.agent/skills/frontend-design/SKILL.md +418 -0
- package/.agent/skills/frontend-design/animation-guide.md +331 -0
- package/.agent/skills/frontend-design/color-system.md +311 -0
- package/.agent/skills/frontend-design/decision-trees.md +418 -0
- package/.agent/skills/frontend-design/motion-graphics.md +306 -0
- package/.agent/skills/frontend-design/scripts/accessibility_checker.py +183 -0
- package/.agent/skills/frontend-design/scripts/ux_audit.py +722 -0
- package/.agent/skills/frontend-design/typography-system.md +345 -0
- package/.agent/skills/frontend-design/ux-psychology.md +1116 -0
- package/.agent/skills/frontend-design/visual-effects.md +383 -0
- package/.agent/skills/i18n-localization/SKILL.md +154 -0
- package/.agent/skills/i18n-localization/scripts/i18n_checker.py +241 -0
- package/.agent/skills/mcp-builder/SKILL.md +176 -0
- package/.agent/skills/web-design-guidelines/SKILL.md +57 -0
- package/.agent/workflows/brainstorm.md +113 -0
- package/.agent/workflows/create.md +59 -0
- package/.agent/workflows/debug.md +103 -0
- package/.agent/workflows/deploy.md +176 -0
- package/.agent/workflows/enhance.md +63 -0
- package/.agent/workflows/orchestrate.md +237 -0
- package/.agent/workflows/plan.md +89 -0
- package/.agent/workflows/preview.md +81 -0
- package/.agent/workflows/simple-test.md +42 -0
- package/.agent/workflows/status.md +86 -0
- package/.agent/workflows/structured-orchestrate.md +180 -0
- package/.agent/workflows/test.md +144 -0
- package/.agent/workflows/ui-ux-pro-max.md +296 -0
- package/.claude/settings.local.json +157 -149
- package/.editorconfig +56 -0
- package/.husky/pre-commit +4 -0
- package/.lintstagedrc +7 -0
- package/.prettierignore +29 -0
- package/.prettierrc +11 -0
- package/CLAUDE.md +2 -0
- package/README.md +64 -55
- package/SPEC.md +102 -61
- package/cli/bin/foliko.js +4 -4
- package/cli/src/commands/chat.js +53 -51
- package/cli/src/commands/list.js +40 -37
- package/cli/src/index.js +18 -18
- package/cli/src/ui/chat-ui.js +78 -76
- package/cli/src/utils/ansi.js +15 -15
- package/cli/src/utils/markdown.js +112 -116
- package/docker-compose.yml +1 -1
- package/docs/ai-sdk-optimization.md +655 -643
- package/docs/features.md +80 -80
- package/docs/quick-reference.md +49 -46
- package/docs/user-manual.md +411 -380
- package/examples/ambient-example.js +95 -97
- package/examples/basic.js +115 -110
- package/examples/bootstrap.js +52 -43
- package/examples/mcp-example.js +56 -53
- package/examples/skill-example.js +49 -49
- package/examples/test-chat.js +60 -58
- package/examples/test-mcp.js +49 -43
- package/examples/test-reload.js +38 -40
- package/examples/test-telegram.js +3 -3
- package/examples/test-tg-bot.js +7 -4
- package/examples/test-tg-simple.js +4 -3
- package/examples/test-tg.js +3 -3
- package/examples/test-think.js +13 -7
- package/examples/test-web-plugin.js +61 -56
- package/examples/test-weixin-feishu.js +40 -37
- package/examples/workflow.js +49 -49
- package/foliko-1.0.75.tgz +0 -0
- package/package.json +37 -3
- package/plugins/ai-plugin.js +7 -5
- package/plugins/ambient-agent/EventWatcher.js +113 -0
- package/plugins/ambient-agent/ExplorerLoop.js +640 -0
- package/plugins/ambient-agent/GoalManager.js +197 -0
- package/plugins/ambient-agent/Reflector.js +95 -0
- package/plugins/ambient-agent/StateStore.js +90 -0
- package/plugins/ambient-agent/constants.js +101 -0
- package/plugins/ambient-agent/index.js +579 -0
- package/plugins/default-plugins.js +62 -49
- package/plugins/email/constants.js +64 -0
- package/plugins/email/handlers.js +461 -0
- package/plugins/email/index.js +278 -0
- package/plugins/email/monitor.js +269 -0
- package/plugins/email/parser.js +138 -0
- package/plugins/email/reply.js +151 -0
- package/plugins/email/utils.js +124 -0
- package/plugins/feishu-plugin.js +23 -19
- package/plugins/file-system-plugin.js +456 -106
- package/plugins/install-plugin.js +6 -4
- package/plugins/python-executor-plugin.js +3 -1
- package/plugins/python-plugin-loader.js +10 -8
- package/plugins/rules-plugin.js +5 -3
- package/plugins/scheduler-plugin.js +18 -16
- package/plugins/session-plugin.js +3 -1
- package/plugins/storage-plugin.js +5 -3
- package/plugins/subagent-plugin.js +152 -92
- package/plugins/telegram-plugin.js +26 -19
- package/plugins/think-plugin.js +4 -2
- package/plugins/tools-plugin.js +3 -1
- package/plugins/web-plugin.js +15 -13
- package/plugins/weixin-plugin.js +43 -36
- package/reports/system-health-report-20260401.md +79 -0
- package/skills/ambient-agent/SKILL.md +49 -39
- package/skills/foliko-dev/AGENTS.md +64 -61
- package/skills/foliko-dev/SKILL.md +125 -119
- package/skills/mcp-usage/SKILL.md +19 -17
- package/skills/python-plugin-dev/SKILL.md +16 -15
- package/skills/skill-guide/SKILL.md +12 -12
- package/skills/subagent-guide/SKILL.md +237 -0
- package/skills/workflow-guide/SKILL.md +90 -45
- package/skills/workflow-troubleshooting/DEBUGGING.md +36 -21
- package/skills/workflow-troubleshooting/SKILL.md +156 -79
- package/src/capabilities/index.js +4 -4
- package/src/capabilities/skill-manager.js +211 -197
- package/src/capabilities/workflow-engine.js +461 -547
- package/src/core/agent-chat.js +426 -279
- package/src/core/agent.js +453 -249
- package/src/core/framework.js +183 -149
- package/src/core/index.js +8 -8
- package/src/core/plugin-base.js +52 -52
- package/src/core/plugin-manager.js +377 -281
- package/src/core/provider.js +35 -32
- package/src/core/sub-agent-config.js +264 -0
- package/src/core/system-prompt-builder.js +120 -0
- package/src/core/tool-registry.js +416 -33
- package/src/core/tool-router.js +149 -68
- package/src/executors/executor-base.js +58 -58
- package/src/executors/mcp-executor.js +269 -257
- package/src/index.js +5 -17
- package/src/utils/circuit-breaker.js +301 -0
- package/src/utils/error-boundary.js +363 -0
- package/src/utils/error.js +374 -0
- package/src/utils/event-emitter.js +20 -20
- package/src/utils/id.js +133 -0
- package/src/utils/index.js +217 -3
- package/src/utils/logger.js +181 -0
- package/src/utils/plugin-helpers.js +90 -0
- package/src/utils/retry.js +122 -0
- package/src/utils/sandbox.js +292 -0
- package/test/tool-registry-validation.test.js +218 -0
- package/test_report.md +70 -0
- package/website/docs/api.html +169 -107
- package/website/docs/configuration.html +296 -144
- package/website/docs/plugin-development.html +154 -85
- package/website/docs/project-structure.html +110 -109
- package/website/docs/skill-development.html +117 -61
- package/website/index.html +209 -205
- package/website/script.js +20 -17
- package/website/styles.css +1 -1
- package/plugins/ambient-agent-plugin.js +0 -1565
- package/plugins/email.js +0 -1142
package/src/core/agent-chat.js
CHANGED
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
* 使用 AI SDK 的 ToolLoopAgent 处理工具调用循环
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
const { EventEmitter } = require('../utils/event-emitter')
|
|
7
|
-
const
|
|
6
|
+
const { EventEmitter } = require('../utils/event-emitter');
|
|
7
|
+
const { logger } = require('../utils/logger');
|
|
8
|
+
const { generateText, pruneMessages } = require('ai');
|
|
8
9
|
|
|
9
10
|
// 模型上下文限制表(留 15-20% 余量给 system prompt 和输出)
|
|
10
11
|
const MODEL_CONTEXT_LIMITS = {
|
|
@@ -22,60 +23,109 @@ const MODEL_CONTEXT_LIMITS = {
|
|
|
22
23
|
'claude-3-5-sonnet': 150000,
|
|
23
24
|
'claude-3-opus': 150000,
|
|
24
25
|
'claude-3-sonnet': 150000,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 纯 JavaScript token 计数器
|
|
30
|
+
* 基于字符数估算,兼容中英文
|
|
31
|
+
* 估算规则:
|
|
32
|
+
* - 英文:约 4 字符 = 1 token
|
|
33
|
+
* - 中文:约 2 字符 = 1 token
|
|
34
|
+
* - 混合文本取加权平均
|
|
35
|
+
*/
|
|
36
|
+
class SimpleTokenizer {
|
|
37
|
+
constructor() {
|
|
38
|
+
// 无需初始化
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 估算文本的 token 数
|
|
43
|
+
* @param {string} text
|
|
44
|
+
* @returns {number}
|
|
45
|
+
*/
|
|
46
|
+
encode(text) {
|
|
47
|
+
if (!text || typeof text !== 'string') {
|
|
48
|
+
return 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// 清理文本
|
|
52
|
+
const cleanText = text
|
|
53
|
+
.replace(/\0+/g, '')
|
|
54
|
+
.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '')
|
|
55
|
+
.slice(0, 100000);
|
|
56
|
+
|
|
57
|
+
if (!cleanText) {
|
|
58
|
+
return 0;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 分离中英文分别计数
|
|
62
|
+
const chineseChars = (cleanText.match(/[\u4e00-\u9fff\u3400-\u4dbf]/g) || []).length;
|
|
63
|
+
const englishChars = cleanText.length - chineseChars;
|
|
64
|
+
|
|
65
|
+
// 中英文分开估算后相加
|
|
66
|
+
// 中文约 2 字符/token,英文约 4 字符/token
|
|
67
|
+
const chineseTokens = chineseChars / 2;
|
|
68
|
+
const englishTokens = englishChars / 4;
|
|
69
|
+
|
|
70
|
+
return Math.ceil(chineseTokens + englishTokens);
|
|
71
|
+
}
|
|
25
72
|
}
|
|
26
73
|
|
|
74
|
+
// 全局 tokenizer 实例
|
|
75
|
+
const _globalTokenizer = new SimpleTokenizer();
|
|
76
|
+
|
|
77
|
+
// 压缩超时时间(毫秒)
|
|
78
|
+
const COMPRESSION_TIMEOUT = 30000;
|
|
79
|
+
|
|
27
80
|
class AgentChatHandler extends EventEmitter {
|
|
28
81
|
/**
|
|
29
82
|
* @param {Agent} agent - Agent 实例
|
|
30
83
|
* @param {Object} config - 配置
|
|
31
84
|
*/
|
|
32
85
|
constructor(agent, config = {}) {
|
|
33
|
-
super()
|
|
86
|
+
super();
|
|
34
87
|
|
|
35
|
-
this.agent = agent
|
|
36
|
-
this.config = config
|
|
88
|
+
this.agent = agent;
|
|
89
|
+
this.config = config;
|
|
37
90
|
|
|
38
|
-
this.model = config.model || 'deepseek-chat'
|
|
39
|
-
this.provider = config.provider || 'deepseek'
|
|
40
|
-
this.apiKey = config.apiKey
|
|
41
|
-
this.baseURL = config.baseURL
|
|
42
|
-
this.providerOptions = config.providerOptions || {}
|
|
91
|
+
this.model = config.model || 'deepseek-chat';
|
|
92
|
+
this.provider = config.provider || 'deepseek';
|
|
93
|
+
this.apiKey = config.apiKey;
|
|
94
|
+
this.baseURL = config.baseURL;
|
|
95
|
+
this.providerOptions = config.providerOptions || {};
|
|
43
96
|
|
|
44
|
-
this._systemPrompt = config.systemPrompt || 'You are a helpful assistant.'
|
|
45
|
-
this._messages = []
|
|
46
|
-
this._tools = new Map()
|
|
47
|
-
this._maxSteps = 5
|
|
97
|
+
this._systemPrompt = config.systemPrompt || 'You are a helpful assistant.';
|
|
98
|
+
this._messages = [];
|
|
99
|
+
this._tools = new Map();
|
|
100
|
+
this._maxSteps = 5; // 降低默认步骤数,减少上下文消耗
|
|
48
101
|
|
|
49
102
|
// 上下文压缩配置:根据模型自动设置限制
|
|
50
|
-
const modelKey = Object.keys(MODEL_CONTEXT_LIMITS).find(k =>
|
|
103
|
+
const modelKey = Object.keys(MODEL_CONTEXT_LIMITS).find((k) =>
|
|
51
104
|
this.model.toLowerCase().includes(k.toLowerCase())
|
|
52
|
-
)
|
|
53
|
-
const defaultLimit = modelKey ? MODEL_CONTEXT_LIMITS[modelKey] : 40000
|
|
54
|
-
this._maxContextTokens = config.maxContextTokens || defaultLimit
|
|
55
|
-
this._compressionThreshold = config.compressionThreshold || 0.6
|
|
56
|
-
this._keepRecentMessages = config.keepRecentMessages || 20
|
|
57
|
-
this._enableSmartCompress = config.enableSmartCompress !== false // 默认开启智能摘要
|
|
58
|
-
this._encoder = null
|
|
59
|
-
this._compressionCount = 0 // 压缩次数统计
|
|
105
|
+
);
|
|
106
|
+
const defaultLimit = modelKey ? MODEL_CONTEXT_LIMITS[modelKey] : 40000;
|
|
107
|
+
this._maxContextTokens = config.maxContextTokens || defaultLimit;
|
|
108
|
+
this._compressionThreshold = config.compressionThreshold || 0.6; // 60% 就压缩,早触发
|
|
109
|
+
this._keepRecentMessages = config.keepRecentMessages || 20; // 保留最近 20 条
|
|
110
|
+
this._enableSmartCompress = config.enableSmartCompress !== false; // 默认开启智能摘要
|
|
111
|
+
this._encoder = null;
|
|
112
|
+
this._compressionCount = 0; // 压缩次数统计
|
|
113
|
+
this._compressionInProgress = false; // 压缩锁,防止并发压缩
|
|
114
|
+
this._compressionPromise = null; // 正在进行的压缩 Promise
|
|
60
115
|
|
|
61
116
|
// 工具结果压缩配置
|
|
62
|
-
this._maxToolResultSize = config.maxToolResultSize || 4000
|
|
117
|
+
this._maxToolResultSize = config.maxToolResultSize || 4000; // 工具结果超过此大小则压缩(字节)
|
|
63
118
|
|
|
64
119
|
// 初始化编码器
|
|
65
|
-
|
|
120
|
+
// 使用纯 JS tokenizer
|
|
121
|
+
this._encoder = _globalTokenizer;
|
|
66
122
|
}
|
|
67
123
|
|
|
68
124
|
/**
|
|
69
|
-
*
|
|
70
|
-
* @private
|
|
125
|
+
* 静态方法:保留接口兼容性(无实际作用)
|
|
71
126
|
*/
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
// cl100k_base 是 GPT-4/ChatGPT 使用的编码
|
|
75
|
-
this._encoder = tiktoken.get_encoding('cl100k_base')
|
|
76
|
-
} catch (err) {
|
|
77
|
-
console.warn('[AgentChat] Failed to initialize tiktoken encoder:', err.message)
|
|
78
|
-
}
|
|
127
|
+
static clearEncoderCache() {
|
|
128
|
+
// 不再需要清理
|
|
79
129
|
}
|
|
80
130
|
|
|
81
131
|
/**
|
|
@@ -85,12 +135,13 @@ class AgentChatHandler extends EventEmitter {
|
|
|
85
135
|
* @private
|
|
86
136
|
*/
|
|
87
137
|
_countTokens(text) {
|
|
88
|
-
if (!
|
|
138
|
+
if (!text || typeof text !== 'string') return 0;
|
|
139
|
+
// SimpleTokenizer.encode 已经处理了清理逻辑
|
|
89
140
|
try {
|
|
90
|
-
return this._encoder.encode(text)
|
|
141
|
+
return this._encoder.encode(text);
|
|
91
142
|
} catch (err) {
|
|
92
|
-
//
|
|
93
|
-
return Math.ceil(text.length / 4)
|
|
143
|
+
// 估算失败时使用字符计数
|
|
144
|
+
return Math.ceil(text.length / 4);
|
|
94
145
|
}
|
|
95
146
|
}
|
|
96
147
|
|
|
@@ -101,21 +152,30 @@ class AgentChatHandler extends EventEmitter {
|
|
|
101
152
|
* @private
|
|
102
153
|
*/
|
|
103
154
|
_countMessagesTokens(messages) {
|
|
104
|
-
let total = 0
|
|
155
|
+
let total = 0;
|
|
105
156
|
for (const msg of messages) {
|
|
106
|
-
|
|
157
|
+
if (!msg) continue;
|
|
158
|
+
total += 4; // role 标记
|
|
107
159
|
if (typeof msg.content === 'string') {
|
|
108
|
-
total += this._countTokens(msg.content)
|
|
160
|
+
total += this._countTokens(msg.content);
|
|
109
161
|
} else if (Array.isArray(msg.content)) {
|
|
110
162
|
for (const part of msg.content) {
|
|
111
|
-
if (part.text) {
|
|
112
|
-
total += this._countTokens(part.text)
|
|
163
|
+
if (part && typeof part === 'object' && part.text) {
|
|
164
|
+
total += this._countTokens(String(part.text));
|
|
113
165
|
}
|
|
114
166
|
}
|
|
167
|
+
} else if (msg.content && typeof msg.content === 'object') {
|
|
168
|
+
// 处理工具结果等对象类型
|
|
169
|
+
try {
|
|
170
|
+
const str = JSON.stringify(msg.content);
|
|
171
|
+
total += this._countTokens(str);
|
|
172
|
+
} catch (e) {
|
|
173
|
+
// 无法序列化的内容,忽略
|
|
174
|
+
}
|
|
115
175
|
}
|
|
116
176
|
}
|
|
117
|
-
total += 4 // 结尾标记
|
|
118
|
-
return total
|
|
177
|
+
total += 4; // 结尾标记
|
|
178
|
+
return total;
|
|
119
179
|
}
|
|
120
180
|
|
|
121
181
|
/**
|
|
@@ -124,8 +184,8 @@ class AgentChatHandler extends EventEmitter {
|
|
|
124
184
|
* @private
|
|
125
185
|
*/
|
|
126
186
|
_shouldCompress() {
|
|
127
|
-
const totalTokens = this._countMessagesTokens(this._messages)
|
|
128
|
-
return totalTokens > this._maxContextTokens * this._compressionThreshold
|
|
187
|
+
const totalTokens = this._countMessagesTokens(this._messages);
|
|
188
|
+
return totalTokens > this._maxContextTokens * this._compressionThreshold;
|
|
129
189
|
}
|
|
130
190
|
|
|
131
191
|
/**
|
|
@@ -134,21 +194,22 @@ class AgentChatHandler extends EventEmitter {
|
|
|
134
194
|
* @private
|
|
135
195
|
*/
|
|
136
196
|
_countToolsTokens() {
|
|
137
|
-
let total = 0
|
|
197
|
+
let total = 0;
|
|
138
198
|
for (const toolDef of this._tools.values()) {
|
|
139
199
|
// 工具名 + 描述
|
|
140
|
-
total += this._countTokens(toolDef.name || '')
|
|
141
|
-
total += this._countTokens(toolDef.description || '')
|
|
200
|
+
total += this._countTokens(String(toolDef.name || ''));
|
|
201
|
+
total += this._countTokens(String(toolDef.description || ''));
|
|
142
202
|
|
|
143
203
|
// 工具参数 schema
|
|
144
204
|
if (toolDef.inputSchema) {
|
|
145
|
-
const schemaStr =
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
205
|
+
const schemaStr =
|
|
206
|
+
typeof toolDef.inputSchema === 'string'
|
|
207
|
+
? toolDef.inputSchema
|
|
208
|
+
: JSON.stringify(toolDef.inputSchema);
|
|
209
|
+
total += this._countTokens(schemaStr);
|
|
149
210
|
}
|
|
150
211
|
}
|
|
151
|
-
return total
|
|
212
|
+
return total;
|
|
152
213
|
}
|
|
153
214
|
|
|
154
215
|
/**
|
|
@@ -157,61 +218,137 @@ class AgentChatHandler extends EventEmitter {
|
|
|
157
218
|
* @private
|
|
158
219
|
*/
|
|
159
220
|
_shouldCompressWithTools() {
|
|
160
|
-
const messagesTokens = this._countMessagesTokens(this._messages)
|
|
161
|
-
const toolsTokens = this._countToolsTokens()
|
|
162
|
-
const systemPromptTokens = this._countTokens(this._systemPrompt)
|
|
163
|
-
const total = messagesTokens + toolsTokens + systemPromptTokens
|
|
221
|
+
const messagesTokens = this._countMessagesTokens(this._messages);
|
|
222
|
+
const toolsTokens = this._countToolsTokens();
|
|
223
|
+
const systemPromptTokens = this._countTokens(this._systemPrompt);
|
|
224
|
+
const total = messagesTokens + toolsTokens + systemPromptTokens;
|
|
164
225
|
|
|
165
226
|
// 如果总token数超过上下文限制的 85%,就压缩
|
|
166
|
-
return total > this._maxContextTokens * 0.85
|
|
227
|
+
return total > this._maxContextTokens * 0.85;
|
|
167
228
|
}
|
|
168
229
|
|
|
169
230
|
/**
|
|
170
|
-
*
|
|
231
|
+
* 压缩上下文消息(智能摘要模式,支持超时控制)
|
|
171
232
|
* 策略:
|
|
172
233
|
* 1. 如果启用了智能摘要且有 AI 客户端,对早期消息进行 AI 总结
|
|
173
234
|
* 2. 否则使用简单裁剪 + 标记
|
|
235
|
+
* 3. 支持超时控制,防止压缩阻塞太久
|
|
174
236
|
* @private
|
|
175
237
|
*/
|
|
176
238
|
async _compressContext() {
|
|
239
|
+
// 如果已经有压缩在进行,返回现有的 Promise
|
|
240
|
+
if (this._compressionInProgress && this._compressionPromise) {
|
|
241
|
+
logger.debug('Compression already in progress, waiting for it...');
|
|
242
|
+
return this._compressionPromise;
|
|
243
|
+
}
|
|
244
|
+
|
|
177
245
|
if (this._messages.length <= this._keepRecentMessages) {
|
|
178
|
-
return
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
this._compressionInProgress = true;
|
|
250
|
+
|
|
251
|
+
// 创建压缩 Promise,带超时控制
|
|
252
|
+
this._compressionPromise = this._executeCompressionWithTimeout().finally(() => {
|
|
253
|
+
this._compressionInProgress = false;
|
|
254
|
+
this._compressionPromise = null;
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
return this._compressionPromise;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* 执行压缩(带超时)
|
|
262
|
+
* @private
|
|
263
|
+
*/
|
|
264
|
+
async _executeCompressionWithTimeout() {
|
|
265
|
+
try {
|
|
266
|
+
return await Promise.race([this._doCompress(), this._createTimeoutPromise()]);
|
|
267
|
+
} catch (err) {
|
|
268
|
+
logger.warn('Compression failed:', err.message);
|
|
269
|
+
// 压缩失败时使用简单的截断策略
|
|
270
|
+
this._simpleCompress();
|
|
179
271
|
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* 创建超时 Promise
|
|
276
|
+
* @private
|
|
277
|
+
*/
|
|
278
|
+
_createTimeoutPromise() {
|
|
279
|
+
return new Promise((_, reject) => {
|
|
280
|
+
setTimeout(() => {
|
|
281
|
+
reject(new Error('Compression timeout'));
|
|
282
|
+
}, COMPRESSION_TIMEOUT);
|
|
283
|
+
});
|
|
284
|
+
}
|
|
180
285
|
|
|
181
|
-
|
|
182
|
-
|
|
286
|
+
/**
|
|
287
|
+
* 执行实际压缩逻辑
|
|
288
|
+
* @private
|
|
289
|
+
*/
|
|
290
|
+
async _doCompress() {
|
|
291
|
+
const systemMessages = this._messages.filter((m) => m.role === 'system');
|
|
292
|
+
const otherMessages = this._messages.filter((m) => m.role !== 'system');
|
|
183
293
|
|
|
184
294
|
// 保留最近的 N 条非系统消息
|
|
185
|
-
const recentMessages = otherMessages.slice(-this._keepRecentMessages)
|
|
186
|
-
const messagesToSummarize = otherMessages.slice(0, -this._keepRecentMessages)
|
|
295
|
+
const recentMessages = otherMessages.slice(-this._keepRecentMessages);
|
|
296
|
+
const messagesToSummarize = otherMessages.slice(0, -this._keepRecentMessages);
|
|
187
297
|
|
|
188
|
-
const compressedCount = messagesToSummarize.length
|
|
189
|
-
let summaryContent = ''
|
|
298
|
+
const compressedCount = messagesToSummarize.length;
|
|
299
|
+
let summaryContent = '';
|
|
190
300
|
|
|
191
301
|
// 尝试使用 AI 总结
|
|
192
302
|
if (this._enableSmartCompress && this._aiClient) {
|
|
193
303
|
try {
|
|
194
|
-
const summaryText = await this._summarizeMessages(messagesToSummarize)
|
|
195
|
-
summaryContent = `[早期对话摘要]: ${summaryText}
|
|
196
|
-
|
|
304
|
+
const summaryText = await this._summarizeMessages(messagesToSummarize);
|
|
305
|
+
summaryContent = `[早期对话摘要]: ${summaryText}`;
|
|
306
|
+
logger.info(`AI summary generated (${summaryText.length} chars)`);
|
|
197
307
|
} catch (err) {
|
|
198
|
-
|
|
199
|
-
summaryContent = `[上下文已压缩: 省略了 ${compressedCount} 条早期消息。保留了最近 ${this._keepRecentMessages} 条对话记录。]
|
|
308
|
+
logger.warn('AI summary failed, using simple compression:', err.message);
|
|
309
|
+
summaryContent = `[上下文已压缩: 省略了 ${compressedCount} 条早期消息。保留了最近 ${this._keepRecentMessages} 条对话记录。]`;
|
|
200
310
|
}
|
|
201
311
|
} else {
|
|
202
|
-
summaryContent = `[上下文已压缩: 省略了 ${compressedCount} 条早期消息。保留了最近 ${this._keepRecentMessages} 条对话记录。]
|
|
312
|
+
summaryContent = `[上下文已压缩: 省略了 ${compressedCount} 条早期消息。保留了最近 ${this._keepRecentMessages} 条对话记录。]`;
|
|
203
313
|
}
|
|
204
314
|
|
|
205
315
|
const summary = {
|
|
206
316
|
role: 'system',
|
|
207
|
-
content: summaryContent
|
|
208
|
-
}
|
|
317
|
+
content: summaryContent,
|
|
318
|
+
};
|
|
209
319
|
|
|
210
|
-
this._messages = [...systemMessages, summary, ...recentMessages]
|
|
211
|
-
this._compressionCount
|
|
320
|
+
this._messages = [...systemMessages, summary, ...recentMessages];
|
|
321
|
+
this._compressionCount++;
|
|
212
322
|
|
|
213
|
-
const totalTokens = this._countMessagesTokens(this._messages)
|
|
214
|
-
|
|
323
|
+
const totalTokens = this._countMessagesTokens(this._messages);
|
|
324
|
+
logger.info(
|
|
325
|
+
`Context compressed (${this._compressionCount} times). Messages: ${this._messages.length}, Est. tokens: ${totalTokens}`
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* 简单压缩(当 AI 压缩失败时使用)
|
|
331
|
+
* @private
|
|
332
|
+
*/
|
|
333
|
+
_simpleCompress() {
|
|
334
|
+
const systemMessages = this._messages.filter((m) => m.role === 'system');
|
|
335
|
+
const otherMessages = this._messages.filter((m) => m.role !== 'system');
|
|
336
|
+
const recentMessages = otherMessages.slice(-this._keepRecentMessages);
|
|
337
|
+
const compressedCount = otherMessages.length - this._keepRecentMessages;
|
|
338
|
+
|
|
339
|
+
const summaryContent = `[上下文已压缩: 省略了 ${compressedCount} 条早期消息。保留了最近 ${this._keepRecentMessages} 条对话记录。]`;
|
|
340
|
+
|
|
341
|
+
const summary = {
|
|
342
|
+
role: 'system',
|
|
343
|
+
content: summaryContent,
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
this._messages = [...systemMessages, summary, ...recentMessages];
|
|
347
|
+
this._compressionCount++;
|
|
348
|
+
|
|
349
|
+
logger.info(
|
|
350
|
+
`Context simple compressed (${this._compressionCount} times). Messages: ${this._messages.length}`
|
|
351
|
+
);
|
|
215
352
|
}
|
|
216
353
|
|
|
217
354
|
/**
|
|
@@ -222,15 +359,17 @@ class AgentChatHandler extends EventEmitter {
|
|
|
222
359
|
*/
|
|
223
360
|
async _summarizeMessages(messages) {
|
|
224
361
|
if (!this._aiClient || messages.length === 0) {
|
|
225
|
-
return '(无早期对话)'
|
|
362
|
+
return '(无早期对话)';
|
|
226
363
|
}
|
|
227
364
|
|
|
228
365
|
// 构建总结提示
|
|
229
|
-
const conversationText = messages
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
366
|
+
const conversationText = messages
|
|
367
|
+
.map((m) => {
|
|
368
|
+
const role = m.role === 'user' ? '用户' : '助手';
|
|
369
|
+
const content = typeof m.content === 'string' ? m.content : JSON.stringify(m.content);
|
|
370
|
+
return `${role}: ${content}`;
|
|
371
|
+
})
|
|
372
|
+
.join('\n');
|
|
234
373
|
|
|
235
374
|
const summarizePrompt = `请简洁地总结以下对话的要点,保留关键信息和用户需求:
|
|
236
375
|
|
|
@@ -240,17 +379,16 @@ ${conversationText}
|
|
|
240
379
|
1. 提取用户的主要需求和意图
|
|
241
380
|
2. 保留关键的技术细节或决策
|
|
242
381
|
3. 不要超过 1000 字
|
|
243
|
-
4.
|
|
382
|
+
4. 用中文回复`;
|
|
244
383
|
|
|
245
384
|
// 使用 AI SDK 6.x 的 generateText
|
|
246
|
-
const { generateText } = require('ai')
|
|
247
385
|
const { text } = await generateText({
|
|
248
386
|
model: this._aiClient,
|
|
249
387
|
prompt: summarizePrompt,
|
|
250
|
-
...this.providerOptions
|
|
251
|
-
})
|
|
388
|
+
...this.providerOptions,
|
|
389
|
+
});
|
|
252
390
|
|
|
253
|
-
return text || '(总结生成失败)'
|
|
391
|
+
return text || '(总结生成失败)';
|
|
254
392
|
}
|
|
255
393
|
|
|
256
394
|
/**
|
|
@@ -260,21 +398,21 @@ ${conversationText}
|
|
|
260
398
|
* @private
|
|
261
399
|
*/
|
|
262
400
|
_shouldCompressToolResult(result) {
|
|
263
|
-
if (!result || this._maxToolResultSize <= 0) return false
|
|
401
|
+
if (!result || this._maxToolResultSize <= 0) return false;
|
|
264
402
|
|
|
265
403
|
// 计算结果的大小
|
|
266
|
-
let size = 0
|
|
404
|
+
let size = 0;
|
|
267
405
|
if (typeof result === 'string') {
|
|
268
|
-
size = result.length
|
|
406
|
+
size = result.length;
|
|
269
407
|
} else if (typeof result === 'object') {
|
|
270
408
|
try {
|
|
271
|
-
size = JSON.stringify(result).length
|
|
409
|
+
size = JSON.stringify(result).length;
|
|
272
410
|
} catch {
|
|
273
|
-
size = String(result).length
|
|
411
|
+
size = String(result).length;
|
|
274
412
|
}
|
|
275
413
|
}
|
|
276
414
|
|
|
277
|
-
return size > this._maxToolResultSize
|
|
415
|
+
return size > this._maxToolResultSize;
|
|
278
416
|
}
|
|
279
417
|
|
|
280
418
|
/**
|
|
@@ -285,28 +423,30 @@ ${conversationText}
|
|
|
285
423
|
*/
|
|
286
424
|
async _compressToolResult(result) {
|
|
287
425
|
if (!this._shouldCompressToolResult(result)) {
|
|
288
|
-
return result
|
|
426
|
+
return result;
|
|
289
427
|
}
|
|
290
428
|
|
|
291
429
|
if (!this._aiClient) {
|
|
292
|
-
|
|
293
|
-
return this._fallbackCompress(result)
|
|
430
|
+
logger.warn('Cannot compress tool result: no AI client');
|
|
431
|
+
return this._fallbackCompress(result);
|
|
294
432
|
}
|
|
295
433
|
|
|
296
434
|
try {
|
|
297
|
-
const originalSize =
|
|
298
|
-
|
|
435
|
+
const originalSize =
|
|
436
|
+
typeof result === 'string' ? result.length : JSON.stringify(result).length;
|
|
437
|
+
const resultStr = typeof result === 'string' ? result : JSON.stringify(result, null, 2);
|
|
299
438
|
|
|
300
439
|
// 对于超大型内容(如网页),采用更好的截断策略
|
|
301
|
-
const maxInputSize = 6000
|
|
302
|
-
const shouldTruncate = resultStr.length > maxInputSize
|
|
303
|
-
const truncatedContent = resultStr.substring(0, maxInputSize)
|
|
304
|
-
const truncatedNote = shouldTruncate ? `\n\n[内容已截断,原始长度 ${originalSize} 字符]` : ''
|
|
440
|
+
const maxInputSize = 6000; // 保留给 AI 处理的输入大小
|
|
441
|
+
const shouldTruncate = resultStr.length > maxInputSize;
|
|
442
|
+
const truncatedContent = resultStr.substring(0, maxInputSize);
|
|
443
|
+
const truncatedNote = shouldTruncate ? `\n\n[内容已截断,原始长度 ${originalSize} 字符]` : '';
|
|
305
444
|
|
|
306
445
|
// 检测内容类型
|
|
307
|
-
const isHTML =
|
|
308
|
-
|
|
309
|
-
const
|
|
446
|
+
const isHTML =
|
|
447
|
+
resultStr.startsWith('<') || resultStr.includes('<html') || resultStr.includes('<!DOCTYPE');
|
|
448
|
+
const isJSON = !isHTML && (resultStr.startsWith('{') || resultStr.startsWith('['));
|
|
449
|
+
const contentTypeHint = isHTML ? '(HTML 网页内容)' : isJSON ? '(JSON 数据)' : '';
|
|
310
450
|
|
|
311
451
|
// 构建压缩提示
|
|
312
452
|
const compressPrompt = `以下是一个工具执行结果${contentTypeHint},长度 ${originalSize} 字符。请简洁地总结其核心内容:
|
|
@@ -318,25 +458,24 @@ ${truncatedContent}${truncatedNote}
|
|
|
318
458
|
2. 关键信息点(不超过 5 个)
|
|
319
459
|
3. 重要数据或结论
|
|
320
460
|
|
|
321
|
-
用简洁的中文总结,不超过 400
|
|
461
|
+
用简洁的中文总结,不超过 400 字:`;
|
|
322
462
|
|
|
323
463
|
// 使用 AI SDK 6.x 的 generateText
|
|
324
|
-
const { generateText } = require('ai')
|
|
325
464
|
const { text } = await generateText({
|
|
326
465
|
model: this._aiClient,
|
|
327
466
|
prompt: compressPrompt,
|
|
328
467
|
...this.providerOptions,
|
|
329
|
-
maxTokens: 500
|
|
330
|
-
})
|
|
468
|
+
maxTokens: 500,
|
|
469
|
+
});
|
|
331
470
|
|
|
332
|
-
const summary = text || '(总结生成失败)'
|
|
333
|
-
const compressed = `[工具结果已压缩${contentTypeHint}: ${originalSize}
|
|
471
|
+
const summary = text || '(总结生成失败)';
|
|
472
|
+
const compressed = `[工具结果已压缩${contentTypeHint}: ${originalSize} -> ${summary.length} 字符]\n\n${summary}`;
|
|
334
473
|
|
|
335
|
-
|
|
336
|
-
return compressed
|
|
474
|
+
logger.info(`Tool result compressed: ${originalSize} -> ${summary.length} chars`);
|
|
475
|
+
return compressed;
|
|
337
476
|
} catch (err) {
|
|
338
|
-
|
|
339
|
-
return this._fallbackCompress(result)
|
|
477
|
+
logger.warn('Tool result compression failed:', err.message);
|
|
478
|
+
return this._fallbackCompress(result);
|
|
340
479
|
}
|
|
341
480
|
}
|
|
342
481
|
|
|
@@ -347,18 +486,18 @@ ${truncatedContent}${truncatedNote}
|
|
|
347
486
|
* @private
|
|
348
487
|
*/
|
|
349
488
|
_fallbackCompress(result) {
|
|
350
|
-
const originalSize = typeof result === 'string' ? result.length : JSON.stringify(result).length
|
|
351
|
-
const resultStr = typeof result === 'string' ? result : JSON.stringify(result, null, 2)
|
|
489
|
+
const originalSize = typeof result === 'string' ? result.length : JSON.stringify(result).length;
|
|
490
|
+
const resultStr = typeof result === 'string' ? result : JSON.stringify(result, null, 2);
|
|
352
491
|
|
|
353
492
|
// 简单截断策略:保留前 2000 字符
|
|
354
|
-
const maxSize = 2000
|
|
493
|
+
const maxSize = 2000;
|
|
355
494
|
if (resultStr.length <= maxSize) {
|
|
356
|
-
return result
|
|
495
|
+
return result;
|
|
357
496
|
}
|
|
358
497
|
|
|
359
|
-
const compressed = `[工具结果已压缩(简单截断): ${originalSize}
|
|
360
|
-
|
|
361
|
-
return compressed
|
|
498
|
+
const compressed = `[工具结果已压缩(简单截断): ${originalSize} -> ${maxSize} 字符]\n\n${resultStr.substring(0, maxSize)}\n\n...[内容已截断,原文 ${originalSize} 字符]`;
|
|
499
|
+
logger.info(`Tool result fallback compressed: ${originalSize} -> ${maxSize} chars`);
|
|
500
|
+
return compressed;
|
|
362
501
|
}
|
|
363
502
|
|
|
364
503
|
/**
|
|
@@ -366,8 +505,8 @@ ${truncatedContent}${truncatedNote}
|
|
|
366
505
|
* @param {Object} client - AI 模型客户端
|
|
367
506
|
*/
|
|
368
507
|
setAIClient(client) {
|
|
369
|
-
this._aiClient = client
|
|
370
|
-
return this
|
|
508
|
+
this._aiClient = client;
|
|
509
|
+
return this;
|
|
371
510
|
}
|
|
372
511
|
|
|
373
512
|
/**
|
|
@@ -375,8 +514,8 @@ ${truncatedContent}${truncatedNote}
|
|
|
375
514
|
* @param {string} prompt
|
|
376
515
|
*/
|
|
377
516
|
setSystemPrompt(prompt) {
|
|
378
|
-
this._systemPrompt = prompt
|
|
379
|
-
return this
|
|
517
|
+
this._systemPrompt = prompt;
|
|
518
|
+
return this;
|
|
380
519
|
}
|
|
381
520
|
|
|
382
521
|
/**
|
|
@@ -384,17 +523,17 @@ ${truncatedContent}${truncatedNote}
|
|
|
384
523
|
* @param {Object} toolDef - 工具定义
|
|
385
524
|
*/
|
|
386
525
|
registerTool(toolDef) {
|
|
387
|
-
this._tools.set(toolDef.name, toolDef)
|
|
388
|
-
return this
|
|
526
|
+
this._tools.set(toolDef.name, toolDef);
|
|
527
|
+
return this;
|
|
389
528
|
}
|
|
390
529
|
|
|
391
530
|
/**
|
|
392
531
|
* 清空对话历史
|
|
393
532
|
*/
|
|
394
533
|
clearHistory() {
|
|
395
|
-
this._messages = []
|
|
396
|
-
this._compressionCount = 0
|
|
397
|
-
return this
|
|
534
|
+
this._messages = [];
|
|
535
|
+
this._compressionCount = 0;
|
|
536
|
+
return this;
|
|
398
537
|
}
|
|
399
538
|
|
|
400
539
|
/**
|
|
@@ -402,7 +541,7 @@ ${truncatedContent}${truncatedNote}
|
|
|
402
541
|
* @returns {Array}
|
|
403
542
|
*/
|
|
404
543
|
getTools() {
|
|
405
|
-
return Array.from(this._tools.values())
|
|
544
|
+
return Array.from(this._tools.values());
|
|
406
545
|
}
|
|
407
546
|
|
|
408
547
|
/**
|
|
@@ -411,13 +550,13 @@ ${truncatedContent}${truncatedNote}
|
|
|
411
550
|
*/
|
|
412
551
|
async _importAI() {
|
|
413
552
|
try {
|
|
414
|
-
const ai = require('ai')
|
|
553
|
+
const ai = require('ai');
|
|
415
554
|
return {
|
|
416
555
|
tool: ai.tool,
|
|
417
|
-
ToolLoopAgent: ai.ToolLoopAgent
|
|
418
|
-
}
|
|
556
|
+
ToolLoopAgent: ai.ToolLoopAgent,
|
|
557
|
+
};
|
|
419
558
|
} catch (err) {
|
|
420
|
-
throw new Error('AI SDK not found. Please install: npm install ai')
|
|
559
|
+
throw new Error('AI SDK not found. Please install: npm install ai');
|
|
421
560
|
}
|
|
422
561
|
}
|
|
423
562
|
|
|
@@ -427,83 +566,90 @@ ${truncatedContent}${truncatedNote}
|
|
|
427
566
|
* @param {Object} options - 选项
|
|
428
567
|
*/
|
|
429
568
|
async chat(message, options = {}) {
|
|
430
|
-
const context = { sessionId: options.sessionId || null, isStream: false }
|
|
431
|
-
const framework = this.agent.framework
|
|
432
|
-
const self = this
|
|
569
|
+
const context = { sessionId: options.sessionId || null, isStream: false };
|
|
570
|
+
const framework = this.agent.framework;
|
|
571
|
+
const self = this; // 保存引用用于回调
|
|
433
572
|
|
|
573
|
+
// 关键:每次 chat 调用时刷新系统提示词,确保包含最新的工具/技能描述
|
|
574
|
+
// 解决上下文过长时 LLM 不调用工具的问题
|
|
575
|
+
this._systemPrompt = this.agent._buildSystemPrompt();
|
|
434
576
|
// 动态导入 AI SDK
|
|
435
|
-
const { tool, ToolLoopAgent } = await this._importAI()
|
|
577
|
+
const { tool, ToolLoopAgent } = await this._importAI();
|
|
436
578
|
|
|
437
|
-
const userMessage = typeof message === 'string'
|
|
438
|
-
? { role: 'user', content: message }
|
|
439
|
-
: message
|
|
579
|
+
const userMessage = typeof message === 'string' ? { role: 'user', content: message } : message;
|
|
440
580
|
|
|
441
|
-
this._messages.push(userMessage)
|
|
581
|
+
this._messages.push(userMessage);
|
|
442
582
|
|
|
443
583
|
// 检查是否需要压缩上下文(包括工具定义)
|
|
444
|
-
const messagesTokens = this._countMessagesTokens(this._messages)
|
|
445
|
-
const toolsTokens = this._countToolsTokens()
|
|
446
|
-
const systemPromptTokens = this._countTokens(this._systemPrompt)
|
|
447
|
-
const totalTokens = messagesTokens + toolsTokens + systemPromptTokens
|
|
448
|
-
const limit = this._maxContextTokens * 0.7
|
|
584
|
+
const messagesTokens = this._countMessagesTokens(this._messages);
|
|
585
|
+
const toolsTokens = this._countToolsTokens();
|
|
586
|
+
const systemPromptTokens = this._countTokens(this._systemPrompt);
|
|
587
|
+
const totalTokens = messagesTokens + toolsTokens + systemPromptTokens;
|
|
588
|
+
const limit = this._maxContextTokens * 0.7; // 降低到 70%,更早压缩
|
|
449
589
|
|
|
450
590
|
if (totalTokens > limit) {
|
|
451
|
-
|
|
452
|
-
|
|
591
|
+
logger.info(
|
|
592
|
+
`Context large (${totalTokens}/${this._maxContextTokens} tokens = msgs:${messagesTokens} + tools:${toolsTokens} + sys:${systemPromptTokens}), compressing...`
|
|
593
|
+
);
|
|
594
|
+
// 使用带超时控制的压缩
|
|
595
|
+
await this._compressContext();
|
|
453
596
|
} else {
|
|
454
|
-
|
|
597
|
+
logger.info(`Context OK: ${totalTokens}/${this._maxContextTokens} tokens`);
|
|
455
598
|
}
|
|
456
599
|
|
|
457
|
-
const maxSteps = options.maxSteps || this._maxSteps
|
|
458
|
-
const tools = this._getAITools(tool)
|
|
600
|
+
const maxSteps = options.maxSteps || this._maxSteps;
|
|
601
|
+
const tools = this._getAITools(tool);
|
|
459
602
|
|
|
460
603
|
if (!this._aiClient) {
|
|
461
|
-
throw new Error('AI client not configured.')
|
|
604
|
+
throw new Error('AI client not configured.');
|
|
462
605
|
}
|
|
463
606
|
|
|
464
607
|
const agent = new ToolLoopAgent({
|
|
465
608
|
model: this._aiClient,
|
|
466
609
|
instructions: this._systemPrompt,
|
|
467
610
|
tools: tools,
|
|
468
|
-
stopWhen: (step) => step.stepCount >= maxSteps
|
|
469
|
-
})
|
|
611
|
+
stopWhen: (step) => step.stepCount >= maxSteps,
|
|
612
|
+
});
|
|
470
613
|
|
|
471
|
-
const messages =
|
|
472
|
-
|
|
473
|
-
|
|
614
|
+
const messages = this._cleanMessages(this._messages);
|
|
615
|
+
const prunedMessages = pruneMessages({
|
|
616
|
+
messages,
|
|
617
|
+
reasoning: 'all', // Remove all reasoning parts
|
|
618
|
+
toolCalls: 'before-last-2-messages', // Remove tool calls except those in the last message
|
|
619
|
+
});
|
|
474
620
|
|
|
475
621
|
try {
|
|
476
622
|
// 使用 runWithContext 让工具执行时能获取 sessionId
|
|
477
623
|
const result = await framework.runWithContext(context, async () => {
|
|
478
|
-
return agent.generate({ messages, ...this.providerOptions })
|
|
479
|
-
})
|
|
624
|
+
return agent.generate({ messages: prunedMessages, ...this.providerOptions });
|
|
625
|
+
});
|
|
480
626
|
|
|
481
627
|
// 处理返回的消息历史:只保留 user 和 assistant 消息,让 SDK 自动处理 tool 消息
|
|
482
628
|
if (result.messages && Array.isArray(result.messages)) {
|
|
483
629
|
// 只保留 user 和 assistant 消息,SDK 会自动维护 tool 消息
|
|
484
|
-
this._messages = result.messages.filter(m => m.role === 'user' || m.role === 'assistant')
|
|
630
|
+
this._messages = result.messages.filter((m) => m.role === 'user' || m.role === 'assistant');
|
|
485
631
|
} else if (result.text) {
|
|
486
632
|
this._messages.push({
|
|
487
633
|
role: 'assistant',
|
|
488
|
-
content: result.text
|
|
489
|
-
})
|
|
634
|
+
content: result.text,
|
|
635
|
+
});
|
|
490
636
|
}
|
|
491
637
|
|
|
492
638
|
// 生成后检查:如果消息太长,下次需要压缩
|
|
493
|
-
const afterTokens = this._countMessagesTokens(this._messages)
|
|
639
|
+
const afterTokens = this._countMessagesTokens(this._messages);
|
|
494
640
|
if (afterTokens > this._maxContextTokens * 0.8) {
|
|
495
|
-
|
|
641
|
+
logger.info(`After generation: ${afterTokens} tokens, will compress on next turn`);
|
|
496
642
|
}
|
|
497
643
|
|
|
498
644
|
return {
|
|
499
645
|
success: true,
|
|
500
646
|
message: result.text || '',
|
|
501
|
-
stepCount: result.stepCount || 1
|
|
502
|
-
}
|
|
647
|
+
stepCount: result.stepCount || 1,
|
|
648
|
+
};
|
|
503
649
|
} catch (err) {
|
|
504
|
-
this.emit('error', { error: err.message })
|
|
650
|
+
this.emit('error', { error: err.message });
|
|
505
651
|
// 抛出错误而不是返回错误响应,让 Agent 能捕获
|
|
506
|
-
throw err
|
|
652
|
+
throw err;
|
|
507
653
|
}
|
|
508
654
|
}
|
|
509
655
|
|
|
@@ -513,98 +659,96 @@ ${truncatedContent}${truncatedNote}
|
|
|
513
659
|
* @param {Object} options - 选项
|
|
514
660
|
*/
|
|
515
661
|
async *chatStream(message, options = {}) {
|
|
516
|
-
const context = { sessionId: options.sessionId || null, isStream: true }
|
|
517
|
-
const framework = this.agent.framework
|
|
662
|
+
const context = { sessionId: options.sessionId || null, isStream: true };
|
|
663
|
+
const framework = this.agent.framework;
|
|
518
664
|
|
|
665
|
+
// 关键:每次 chat 调用时刷新系统提示词,确保包含最新的工具/技能描述
|
|
666
|
+
// 解决上下文过长时 LLM 不调用工具的问题
|
|
667
|
+
this._systemPrompt = this.agent._buildSystemPrompt();
|
|
519
668
|
// 动态导入 AI SDK
|
|
520
|
-
const { tool, ToolLoopAgent } = await this._importAI()
|
|
521
|
-
|
|
522
|
-
const userMessage = typeof message === 'string'
|
|
523
|
-
? { role: 'user', content: message }
|
|
524
|
-
: message
|
|
525
|
-
|
|
526
|
-
this._messages.push(userMessage)
|
|
669
|
+
const { tool, ToolLoopAgent } = await this._importAI();
|
|
527
670
|
|
|
671
|
+
const userMessage = typeof message === 'string' ? { role: 'user', content: message } : message;
|
|
672
|
+
// console.log('System Prompt:', this._systemPrompt);
|
|
673
|
+
this._messages.push(userMessage);
|
|
528
674
|
// 检查是否需要压缩上下文(包括工具定义)
|
|
529
|
-
const messagesTokens = this._countMessagesTokens(this._messages)
|
|
530
|
-
const toolsTokens = this._countToolsTokens()
|
|
531
|
-
const systemPromptTokens = this._countTokens(this._systemPrompt)
|
|
532
|
-
const totalTokens = messagesTokens + toolsTokens + systemPromptTokens
|
|
533
|
-
const limit = this._maxContextTokens * 0.7
|
|
675
|
+
const messagesTokens = this._countMessagesTokens(this._messages);
|
|
676
|
+
const toolsTokens = this._countToolsTokens();
|
|
677
|
+
const systemPromptTokens = this._countTokens(this._systemPrompt);
|
|
678
|
+
const totalTokens = messagesTokens + toolsTokens + systemPromptTokens;
|
|
679
|
+
const limit = this._maxContextTokens * 0.7; // 降低到 70%
|
|
534
680
|
|
|
535
681
|
// 对于流式调用,如果上下文太大,先压缩再开始
|
|
536
682
|
if (totalTokens > limit) {
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
683
|
+
logger.info(
|
|
684
|
+
`Context large (${totalTokens}/${this._maxContextTokens} tokens), compressing...`
|
|
685
|
+
);
|
|
686
|
+
// 流式调用时等待压缩完成(使用带超时控制的压缩)
|
|
687
|
+
await this._compressContext();
|
|
540
688
|
}
|
|
541
689
|
|
|
542
|
-
const maxSteps = options.maxSteps || this._maxSteps
|
|
543
|
-
const tools = this._getAITools(tool)
|
|
544
|
-
const self = this
|
|
690
|
+
const maxSteps = options.maxSteps || this._maxSteps;
|
|
691
|
+
const tools = this._getAITools(tool);
|
|
692
|
+
const self = this; // 保存引用用于回调
|
|
545
693
|
|
|
546
694
|
if (!this._aiClient) {
|
|
547
|
-
throw new Error('AI client not configured.')
|
|
695
|
+
throw new Error('AI client not configured.');
|
|
548
696
|
}
|
|
549
697
|
|
|
550
698
|
const agent = new ToolLoopAgent({
|
|
551
699
|
model: this._aiClient,
|
|
552
700
|
instructions: this._systemPrompt,
|
|
553
701
|
tools: tools,
|
|
554
|
-
stopWhen: (step) => step.stepCount >= maxSteps
|
|
555
|
-
})
|
|
556
|
-
|
|
557
|
-
const messages =
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
702
|
+
stopWhen: (step) => step.stepCount >= maxSteps,
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
const messages = this._cleanMessages(this._messages);
|
|
706
|
+
const prunedMessages = pruneMessages({
|
|
707
|
+
messages,
|
|
708
|
+
reasoning: 'all', // Remove all reasoning parts
|
|
709
|
+
toolCalls: 'before-last-2-messages', // Remove tool calls except those in the last message
|
|
710
|
+
});
|
|
561
711
|
try {
|
|
562
712
|
// 使用 runWithContext 让工具执行时能获取 sessionId(支持并行)
|
|
563
713
|
const result = await framework.runWithContext(context, async () => {
|
|
564
|
-
return agent.stream({ messages, ...this.providerOptions })
|
|
565
|
-
})
|
|
714
|
+
return agent.stream({ messages: prunedMessages, ...this.providerOptions });
|
|
715
|
+
});
|
|
566
716
|
|
|
567
|
-
const stream = result.fullStream
|
|
568
|
-
let fullText = ''
|
|
717
|
+
const stream = result.fullStream;
|
|
718
|
+
let fullText = '';
|
|
569
719
|
|
|
570
720
|
// 流式迭代器
|
|
571
|
-
const iterator = stream[Symbol.asyncIterator] ? stream : stream.fullStream
|
|
572
|
-
const finalMessages = []
|
|
721
|
+
const iterator = stream[Symbol.asyncIterator] ? stream : stream.fullStream;
|
|
722
|
+
const finalMessages = [];
|
|
573
723
|
|
|
574
|
-
for await (const part of
|
|
724
|
+
for await (const part of iterator || stream) {
|
|
575
725
|
if (part.type === 'text-delta') {
|
|
576
|
-
const text = part.text || part.textDelta || ''
|
|
577
|
-
fullText += text
|
|
578
|
-
yield { type: 'text', text }
|
|
726
|
+
const text = part.text || part.textDelta || '';
|
|
727
|
+
fullText += text;
|
|
728
|
+
yield { type: 'text', text };
|
|
579
729
|
} else if (part.type === 'reasoning') {
|
|
580
|
-
|
|
730
|
+
finalMessages.push(part);
|
|
731
|
+
this.emit('thinking', { content: part.text });
|
|
581
732
|
} else if (part.type === 'tool-call') {
|
|
582
|
-
|
|
733
|
+
finalMessages.push(part);
|
|
734
|
+
yield { type: 'tool-call', toolName: part.toolName, args: part.input };
|
|
583
735
|
} else if (part.type === 'tool-result') {
|
|
584
|
-
//
|
|
585
|
-
|
|
586
|
-
|
|
736
|
+
// AI SDK 6.x 要求 tool 消息格式为:
|
|
737
|
+
// { role: 'tool', content: [{ type: 'tool-result', toolCallId, toolName, output }] }
|
|
738
|
+
finalMessages.push(part); // 先保存到 finalMessages,等生成结束后统一添加到历史
|
|
739
|
+
yield { type: 'tool-result', toolName: part.toolName, result: part.output };
|
|
587
740
|
} else if (part.type === 'error') {
|
|
588
|
-
yield { type: 'error', error: part.error }
|
|
741
|
+
yield { type: 'error', error: part.error };
|
|
589
742
|
}
|
|
590
743
|
}
|
|
591
744
|
|
|
592
|
-
|
|
593
|
-
//
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
// if (contentSize > self._maxToolResultSize) {
|
|
597
|
-
// msg.content = await self._compressToolResult(msg.content)
|
|
598
|
-
// }
|
|
599
|
-
// }
|
|
600
|
-
// }
|
|
601
|
-
|
|
602
|
-
// 只保留 user 和 assistant 消息,让 SDK 自动处理 tool 消息
|
|
603
|
-
const assistantMsg = { role: 'assistant', content: fullText }
|
|
604
|
-
this._messages.push(assistantMsg)
|
|
745
|
+
this._messages.push({ role: 'tool', content: finalMessages });
|
|
746
|
+
// 添加 assistant 消息
|
|
747
|
+
const assistantMsg = { role: 'assistant', content: fullText };
|
|
748
|
+
this._messages.push(assistantMsg);
|
|
605
749
|
} catch (err) {
|
|
606
|
-
this.emit('error', { error: err.message })
|
|
607
|
-
yield { type: 'error', error: err.message }
|
|
750
|
+
this.emit('error', { error: err.message });
|
|
751
|
+
yield { type: 'error', error: err.message };
|
|
608
752
|
}
|
|
609
753
|
}
|
|
610
754
|
|
|
@@ -615,10 +759,10 @@ ${truncatedContent}${truncatedNote}
|
|
|
615
759
|
* @private
|
|
616
760
|
*/
|
|
617
761
|
_getAITools(toolFn) {
|
|
618
|
-
const tools = {}
|
|
762
|
+
const tools = {};
|
|
619
763
|
|
|
620
764
|
for (const [name, toolDef] of this._tools) {
|
|
621
|
-
const toolName = toolDef.name || name
|
|
765
|
+
const toolName = toolDef.name || name;
|
|
622
766
|
|
|
623
767
|
// 使用 AI SDK 的 tool() 格式
|
|
624
768
|
// 支持 inputSchema (zod schema) 或 parameters (旧格式)
|
|
@@ -627,33 +771,33 @@ ${truncatedContent}${truncatedNote}
|
|
|
627
771
|
description: toolDef.description || '',
|
|
628
772
|
execute: async (args) => {
|
|
629
773
|
// 清理参数:移除 undefined、function 等无效值
|
|
630
|
-
const cleanedArgs = this._cleanToolArgs(args)
|
|
774
|
+
const cleanedArgs = this._cleanToolArgs(args);
|
|
631
775
|
|
|
632
776
|
// 执行工具
|
|
633
|
-
this.emit('tool-call', { name: toolName, args: cleanedArgs })
|
|
777
|
+
this.emit('tool-call', { name: toolName, args: cleanedArgs });
|
|
634
778
|
try {
|
|
635
|
-
const result = await toolDef.execute(cleanedArgs, this.agent.framework)
|
|
636
|
-
this.emit('tool-result', { name: toolName, args: cleanedArgs, result })
|
|
637
|
-
return result
|
|
779
|
+
const result = await toolDef.execute(cleanedArgs, this.agent.framework);
|
|
780
|
+
this.emit('tool-result', { name: toolName, args: cleanedArgs, result });
|
|
781
|
+
return result;
|
|
638
782
|
} catch (err) {
|
|
639
|
-
this.emit('tool-error', { name: toolName, args: cleanedArgs, error: err.message })
|
|
640
|
-
return { error: err.message }
|
|
783
|
+
this.emit('tool-error', { name: toolName, args: cleanedArgs, error: err.message });
|
|
784
|
+
return { error: err.message };
|
|
641
785
|
}
|
|
642
|
-
}
|
|
643
|
-
}
|
|
786
|
+
},
|
|
787
|
+
};
|
|
644
788
|
|
|
645
789
|
// 支持 inputSchema (zod) 或 parameters (旧格式)
|
|
646
790
|
if (toolDef.inputSchema) {
|
|
647
|
-
toolConfig.inputSchema = toolDef.inputSchema
|
|
791
|
+
toolConfig.inputSchema = toolDef.inputSchema;
|
|
648
792
|
} else if (toolDef.parameters) {
|
|
649
|
-
toolConfig.parameters = toolDef.parameters
|
|
793
|
+
toolConfig.parameters = toolDef.parameters;
|
|
650
794
|
}
|
|
651
795
|
|
|
652
796
|
// AI SDK 6.x 使用对象形式,键为工具名
|
|
653
|
-
tools[toolName] = toolFn(toolConfig)
|
|
797
|
+
tools[toolName] = toolFn(toolConfig);
|
|
654
798
|
}
|
|
655
799
|
|
|
656
|
-
return tools
|
|
800
|
+
return tools;
|
|
657
801
|
}
|
|
658
802
|
|
|
659
803
|
/**
|
|
@@ -664,32 +808,32 @@ ${truncatedContent}${truncatedNote}
|
|
|
664
808
|
*/
|
|
665
809
|
_cleanToolArgs(args) {
|
|
666
810
|
if (!args || typeof args !== 'object') {
|
|
667
|
-
return {}
|
|
811
|
+
return {};
|
|
668
812
|
}
|
|
669
813
|
|
|
670
|
-
const cleaned = {}
|
|
814
|
+
const cleaned = {};
|
|
671
815
|
for (const [key, value] of Object.entries(args)) {
|
|
672
816
|
// 跳过 undefined、function、symbol 等无效值
|
|
673
817
|
if (value === undefined || value === null) {
|
|
674
|
-
continue
|
|
818
|
+
continue;
|
|
675
819
|
}
|
|
676
820
|
if (typeof value === 'function' || typeof value === 'symbol') {
|
|
677
|
-
continue
|
|
821
|
+
continue;
|
|
678
822
|
}
|
|
679
823
|
// 递归清理嵌套对象
|
|
680
824
|
if (typeof value === 'object' && !Array.isArray(value)) {
|
|
681
|
-
cleaned[key] = this._cleanToolArgs(value)
|
|
825
|
+
cleaned[key] = this._cleanToolArgs(value);
|
|
682
826
|
} else if (Array.isArray(value)) {
|
|
683
|
-
cleaned[key] = value.map(item =>
|
|
684
|
-
typeof item === 'object' && item !== null
|
|
685
|
-
? this._cleanToolArgs(item)
|
|
686
|
-
: item
|
|
687
|
-
).filter(item => item !== undefined && typeof item !== 'function')
|
|
688
|
-
} else {
|
|
689
827
|
cleaned[key] = value
|
|
828
|
+
.map((item) =>
|
|
829
|
+
typeof item === 'object' && item !== null ? this._cleanToolArgs(item) : item
|
|
830
|
+
)
|
|
831
|
+
.filter((item) => item !== undefined && typeof item !== 'function');
|
|
832
|
+
} else {
|
|
833
|
+
cleaned[key] = value;
|
|
690
834
|
}
|
|
691
835
|
}
|
|
692
|
-
return cleaned
|
|
836
|
+
return cleaned;
|
|
693
837
|
}
|
|
694
838
|
|
|
695
839
|
/**
|
|
@@ -697,39 +841,42 @@ ${truncatedContent}${truncatedNote}
|
|
|
697
841
|
* @private
|
|
698
842
|
*/
|
|
699
843
|
_cleanMessages(messages) {
|
|
700
|
-
return messages.map(msg => {
|
|
844
|
+
return messages.map((msg) => {
|
|
701
845
|
if (!msg || typeof msg !== 'object') {
|
|
702
|
-
return { role: 'user', content: '' }
|
|
846
|
+
return { role: 'user', content: '' };
|
|
703
847
|
}
|
|
704
848
|
|
|
705
849
|
const cleaned = {
|
|
706
|
-
role: msg.role || 'user'
|
|
707
|
-
}
|
|
850
|
+
role: msg.role || 'user',
|
|
851
|
+
};
|
|
708
852
|
|
|
709
853
|
if (Array.isArray(msg.content)) {
|
|
710
|
-
cleaned.content = msg.content
|
|
711
|
-
} else if (msg.content
|
|
712
|
-
cleaned.content = msg.content
|
|
854
|
+
cleaned.content = msg.content;
|
|
855
|
+
} else if (typeof msg.content === 'string') {
|
|
856
|
+
cleaned.content = msg.content;
|
|
857
|
+
} else if (typeof msg.content === 'object' && msg.content !== null) {
|
|
858
|
+
// 对象类型的 content(如 tool result),转为字符串
|
|
859
|
+
cleaned.content =
|
|
860
|
+
typeof msg.content === 'object' && msg.content.content !== undefined
|
|
861
|
+
? String(msg.content.content)
|
|
862
|
+
: JSON.stringify(msg.content);
|
|
713
863
|
} else {
|
|
714
|
-
cleaned.content = msg.text || msg.input || ''
|
|
864
|
+
cleaned.content = msg.text || msg.input || '';
|
|
715
865
|
}
|
|
716
866
|
|
|
717
|
-
return cleaned
|
|
718
|
-
})
|
|
867
|
+
return cleaned;
|
|
868
|
+
});
|
|
719
869
|
}
|
|
720
870
|
|
|
721
871
|
/**
|
|
722
872
|
* 销毁
|
|
723
873
|
*/
|
|
724
874
|
destroy() {
|
|
725
|
-
this._messages = []
|
|
726
|
-
this._tools.clear()
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
this._encoder = null
|
|
730
|
-
}
|
|
731
|
-
this.removeAllListeners()
|
|
875
|
+
this._messages = [];
|
|
876
|
+
this._tools.clear();
|
|
877
|
+
this._encoder = null;
|
|
878
|
+
this.removeAllListeners();
|
|
732
879
|
}
|
|
733
880
|
}
|
|
734
881
|
|
|
735
|
-
module.exports = { AgentChatHandler }
|
|
882
|
+
module.exports = { AgentChatHandler };
|