foliko 1.0.49 → 1.0.51
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/.claude/settings.local.json +5 -1
- package/examples/basic.js +110 -110
- package/examples/mcp-example.js +53 -53
- package/examples/skill-example.js +49 -49
- package/examples/test-mcp.js +79 -79
- package/examples/test-reload.js +61 -61
- package/package.json +2 -1
- package/plugins/session-plugin.js +1 -1
- package/src/core/agent-chat.js +179 -2
- package/src/core/agent.js +6 -1
- package/src/executors/executor-base.js +58 -58
- package/test-server.js +25 -25
- package/test.txt +3 -3
|
@@ -116,7 +116,11 @@
|
|
|
116
116
|
"Bash(taskkill //F //PID 19848)",
|
|
117
117
|
"Bash(curl -s -X POST \"http://localhost:3000/webhook/075s5s2umn4smn4f\" \\\\\n -H \"Content-Type: application/json\" \\\\\n -d '{\"test\":\"data\"}' 2>&1)",
|
|
118
118
|
"Bash(curl -s http://localhost:3000/api/hello 2>&1)",
|
|
119
|
-
"Bash(curl -v -X POST \"http://localhost:3000/webhook/075s5s2umn4smn4f\" \\\\\n -H \"Content-Type: application/json\" \\\\\n -d '{\"test\":\"data\"}' 2>&1 | head -30)"
|
|
119
|
+
"Bash(curl -v -X POST \"http://localhost:3000/webhook/075s5s2umn4smn4f\" \\\\\n -H \"Content-Type: application/json\" \\\\\n -d '{\"test\":\"data\"}' 2>&1 | head -30)",
|
|
120
|
+
"Bash(cd D:/code/vb-agent && timeout 30 node test-compression.js 2>&1)",
|
|
121
|
+
"Bash(cd D:/code/vb-agent && node -c src/core/agent-chat.js 2>&1)",
|
|
122
|
+
"Bash(cd D:/code/vb-agent && node -e \"require\\('./src/core/agent-chat.js'\\); console.log\\('模块加载成功'\\)\" 2>&1)",
|
|
123
|
+
"Bash(cd D:/code/vb-agent && node -e \"require\\('./src/core/agent.js'\\); console.log\\('Agent 加载成功'\\)\" 2>&1)"
|
|
120
124
|
]
|
|
121
125
|
}
|
|
122
126
|
}
|
package/examples/basic.js
CHANGED
|
@@ -1,110 +1,110 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 基础示例
|
|
3
|
-
* 展示如何使用 Framework 和 Agent
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const { Framework } = require('../src')
|
|
7
|
-
const { AIPlugin } = require('../plugins/ai-plugin')
|
|
8
|
-
const { z } = require('zod')
|
|
9
|
-
|
|
10
|
-
async function main() {
|
|
11
|
-
// 创建框架实例
|
|
12
|
-
const framework = new Framework({ debug: true })
|
|
13
|
-
|
|
14
|
-
// 加载 AI 插件
|
|
15
|
-
await framework.loadPlugin(new AIPlugin({
|
|
16
|
-
provider: 'deepseek',
|
|
17
|
-
model: 'deepseek-chat',
|
|
18
|
-
apiKey: process.env.DEEPSEEK_API_KEY || 'your-api-key'
|
|
19
|
-
}))
|
|
20
|
-
|
|
21
|
-
// 注册自定义工具(使用 inputSchema 格式)
|
|
22
|
-
framework.registerTool({
|
|
23
|
-
name: 'hello',
|
|
24
|
-
description: '打招呼工具',
|
|
25
|
-
inputSchema: z.object({
|
|
26
|
-
name: z.string().optional().describe('姓名')
|
|
27
|
-
}),
|
|
28
|
-
execute: async (args) => {
|
|
29
|
-
return `Hello, ${args.name || 'World'}!`
|
|
30
|
-
}
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
// 注册计算器工具
|
|
34
|
-
framework.registerTool({
|
|
35
|
-
name: 'calculate',
|
|
36
|
-
description: '简单的计算器',
|
|
37
|
-
inputSchema: z.object({
|
|
38
|
-
expression: z.string().describe('数学表达式,如 2+3*4')
|
|
39
|
-
}),
|
|
40
|
-
execute: async (args) => {
|
|
41
|
-
try {
|
|
42
|
-
// 安全计算(仅支持基本运算)
|
|
43
|
-
const result = Function(`"use strict"; return (${args.expression})`)()
|
|
44
|
-
return { result }
|
|
45
|
-
} catch (e) {
|
|
46
|
-
return { error: e.message }
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
console.log('[Framework] Ready!')
|
|
52
|
-
console.log('[Tools]', framework.getTools().map(t => t.name))
|
|
53
|
-
|
|
54
|
-
// 创建 Agent
|
|
55
|
-
const agent = framework.createAgent({
|
|
56
|
-
name: 'MyAgent',
|
|
57
|
-
systemPrompt: '你是一个有帮助的助手。当需要计算时,使用 calculate 工具。'
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
// 监听事件
|
|
61
|
-
agent.on('tool-call', (tool) => {
|
|
62
|
-
console.log('[Agent] Tool call:', tool.name, tool.args)
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
agent.on('tool-result', (result) => {
|
|
66
|
-
console.log('[Agent] Tool result:', result.name, result.result)
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
// AI 对话示例
|
|
70
|
-
console.log('\n=== AI Chat Example ===')
|
|
71
|
-
try {
|
|
72
|
-
const response = await agent.chat('你好!')
|
|
73
|
-
console.log('[Agent] Response:', response.message)
|
|
74
|
-
} catch (err) {
|
|
75
|
-
console.error('[Agent] Error:', err.message)
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// 使用工具的对话示例
|
|
79
|
-
console.log('\n=== AI Chat with Tool Call ===')
|
|
80
|
-
try {
|
|
81
|
-
const response = await agent.chat('请帮我计算 (15 + 25) * 2 等于多少?')
|
|
82
|
-
console.log('[Agent] Response:', response.message)
|
|
83
|
-
} catch (err) {
|
|
84
|
-
console.error('[Agent] Error:', err.message)
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// 流式对话示例
|
|
88
|
-
console.log('\n=== Streaming Chat ===')
|
|
89
|
-
try {
|
|
90
|
-
for await (const chunk of agent.chatStream('请用中文介绍一下你自己')) {
|
|
91
|
-
if (chunk.type === 'text') {
|
|
92
|
-
process.stdout.write(chunk.text)
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
console.log('\n')
|
|
96
|
-
} catch (err) {
|
|
97
|
-
console.error('[Agent] Stream Error:', err.message)
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// 热重载示例
|
|
101
|
-
console.log('\n=== Hot Reload ===')
|
|
102
|
-
await framework.reloadPlugin('ai')
|
|
103
|
-
console.log('AI plugin reloaded!')
|
|
104
|
-
|
|
105
|
-
// 清理
|
|
106
|
-
await framework.destroy()
|
|
107
|
-
console.log('\n[Done]')
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
main().catch(console.error)
|
|
1
|
+
/**
|
|
2
|
+
* 基础示例
|
|
3
|
+
* 展示如何使用 Framework 和 Agent
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { Framework } = require('../src')
|
|
7
|
+
const { AIPlugin } = require('../plugins/ai-plugin')
|
|
8
|
+
const { z } = require('zod')
|
|
9
|
+
|
|
10
|
+
async function main() {
|
|
11
|
+
// 创建框架实例
|
|
12
|
+
const framework = new Framework({ debug: true })
|
|
13
|
+
|
|
14
|
+
// 加载 AI 插件
|
|
15
|
+
await framework.loadPlugin(new AIPlugin({
|
|
16
|
+
provider: 'deepseek',
|
|
17
|
+
model: 'deepseek-chat',
|
|
18
|
+
apiKey: process.env.DEEPSEEK_API_KEY || 'your-api-key'
|
|
19
|
+
}))
|
|
20
|
+
|
|
21
|
+
// 注册自定义工具(使用 inputSchema 格式)
|
|
22
|
+
framework.registerTool({
|
|
23
|
+
name: 'hello',
|
|
24
|
+
description: '打招呼工具',
|
|
25
|
+
inputSchema: z.object({
|
|
26
|
+
name: z.string().optional().describe('姓名')
|
|
27
|
+
}),
|
|
28
|
+
execute: async (args) => {
|
|
29
|
+
return `Hello, ${args.name || 'World'}!`
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
// 注册计算器工具
|
|
34
|
+
framework.registerTool({
|
|
35
|
+
name: 'calculate',
|
|
36
|
+
description: '简单的计算器',
|
|
37
|
+
inputSchema: z.object({
|
|
38
|
+
expression: z.string().describe('数学表达式,如 2+3*4')
|
|
39
|
+
}),
|
|
40
|
+
execute: async (args) => {
|
|
41
|
+
try {
|
|
42
|
+
// 安全计算(仅支持基本运算)
|
|
43
|
+
const result = Function(`"use strict"; return (${args.expression})`)()
|
|
44
|
+
return { result }
|
|
45
|
+
} catch (e) {
|
|
46
|
+
return { error: e.message }
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
console.log('[Framework] Ready!')
|
|
52
|
+
console.log('[Tools]', framework.getTools().map(t => t.name))
|
|
53
|
+
|
|
54
|
+
// 创建 Agent
|
|
55
|
+
const agent = framework.createAgent({
|
|
56
|
+
name: 'MyAgent',
|
|
57
|
+
systemPrompt: '你是一个有帮助的助手。当需要计算时,使用 calculate 工具。'
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
// 监听事件
|
|
61
|
+
agent.on('tool-call', (tool) => {
|
|
62
|
+
console.log('[Agent] Tool call:', tool.name, tool.args)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
agent.on('tool-result', (result) => {
|
|
66
|
+
console.log('[Agent] Tool result:', result.name, result.result)
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
// AI 对话示例
|
|
70
|
+
console.log('\n=== AI Chat Example ===')
|
|
71
|
+
try {
|
|
72
|
+
const response = await agent.chat('你好!')
|
|
73
|
+
console.log('[Agent] Response:', response.message)
|
|
74
|
+
} catch (err) {
|
|
75
|
+
console.error('[Agent] Error:', err.message)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// 使用工具的对话示例
|
|
79
|
+
console.log('\n=== AI Chat with Tool Call ===')
|
|
80
|
+
try {
|
|
81
|
+
const response = await agent.chat('请帮我计算 (15 + 25) * 2 等于多少?')
|
|
82
|
+
console.log('[Agent] Response:', response.message)
|
|
83
|
+
} catch (err) {
|
|
84
|
+
console.error('[Agent] Error:', err.message)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// 流式对话示例
|
|
88
|
+
console.log('\n=== Streaming Chat ===')
|
|
89
|
+
try {
|
|
90
|
+
for await (const chunk of agent.chatStream('请用中文介绍一下你自己')) {
|
|
91
|
+
if (chunk.type === 'text') {
|
|
92
|
+
process.stdout.write(chunk.text)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
console.log('\n')
|
|
96
|
+
} catch (err) {
|
|
97
|
+
console.error('[Agent] Stream Error:', err.message)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// 热重载示例
|
|
101
|
+
console.log('\n=== Hot Reload ===')
|
|
102
|
+
await framework.reloadPlugin('ai')
|
|
103
|
+
console.log('AI plugin reloaded!')
|
|
104
|
+
|
|
105
|
+
// 清理
|
|
106
|
+
await framework.destroy()
|
|
107
|
+
console.log('\n[Done]')
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
main().catch(console.error)
|
package/examples/mcp-example.js
CHANGED
|
@@ -1,53 +1,53 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MCP 执行器示例
|
|
3
|
-
* 展示如何连接 MCP 服务器
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const { Framework } = require('../src')
|
|
7
|
-
const { MCPExecutorPlugin } = require('../src/executors/mcp-executor')
|
|
8
|
-
|
|
9
|
-
async function main() {
|
|
10
|
-
console.log('=== MCP Executor Example ===\n')
|
|
11
|
-
|
|
12
|
-
// 创建框架
|
|
13
|
-
const framework = new Framework({ debug: true })
|
|
14
|
-
|
|
15
|
-
// 创建 MCP 执行器插件
|
|
16
|
-
const mcpPlugin = new MCPExecutorPlugin({
|
|
17
|
-
servers: [
|
|
18
|
-
// 示例:添加一个 MCP 服务器
|
|
19
|
-
// {
|
|
20
|
-
// name: 'example',
|
|
21
|
-
// command: 'uvx',
|
|
22
|
-
// args: ['example-mcp-server'],
|
|
23
|
-
// env: {}
|
|
24
|
-
// }
|
|
25
|
-
]
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
// 加载插件
|
|
29
|
-
await framework.loadPlugin(mcpPlugin)
|
|
30
|
-
|
|
31
|
-
// 列出服务器
|
|
32
|
-
console.log('\n--- MCP Servers ---')
|
|
33
|
-
const servers = mcpPlugin.getServers()
|
|
34
|
-
console.log('Servers:', servers)
|
|
35
|
-
|
|
36
|
-
// 列出所有 MCP 工具
|
|
37
|
-
console.log('\n--- MCP Tools ---')
|
|
38
|
-
const tools = framework.getTools().filter(t => t.name.startsWith('mcp_'))
|
|
39
|
-
console.log('MCP tools:', tools.map(t => t.name))
|
|
40
|
-
|
|
41
|
-
// 如果有配置的服务器,可以这样调用:
|
|
42
|
-
// const result = await framework.executeTool('mcp_call', {
|
|
43
|
-
// server: 'example',
|
|
44
|
-
// tool: 'tool_name',
|
|
45
|
-
// args: { /* tool arguments */ }
|
|
46
|
-
// })
|
|
47
|
-
|
|
48
|
-
// 清理
|
|
49
|
-
await framework.destroy()
|
|
50
|
-
console.log('\n[Done]')
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
main().catch(console.error)
|
|
1
|
+
/**
|
|
2
|
+
* MCP 执行器示例
|
|
3
|
+
* 展示如何连接 MCP 服务器
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { Framework } = require('../src')
|
|
7
|
+
const { MCPExecutorPlugin } = require('../src/executors/mcp-executor')
|
|
8
|
+
|
|
9
|
+
async function main() {
|
|
10
|
+
console.log('=== MCP Executor Example ===\n')
|
|
11
|
+
|
|
12
|
+
// 创建框架
|
|
13
|
+
const framework = new Framework({ debug: true })
|
|
14
|
+
|
|
15
|
+
// 创建 MCP 执行器插件
|
|
16
|
+
const mcpPlugin = new MCPExecutorPlugin({
|
|
17
|
+
servers: [
|
|
18
|
+
// 示例:添加一个 MCP 服务器
|
|
19
|
+
// {
|
|
20
|
+
// name: 'example',
|
|
21
|
+
// command: 'uvx',
|
|
22
|
+
// args: ['example-mcp-server'],
|
|
23
|
+
// env: {}
|
|
24
|
+
// }
|
|
25
|
+
]
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
// 加载插件
|
|
29
|
+
await framework.loadPlugin(mcpPlugin)
|
|
30
|
+
|
|
31
|
+
// 列出服务器
|
|
32
|
+
console.log('\n--- MCP Servers ---')
|
|
33
|
+
const servers = mcpPlugin.getServers()
|
|
34
|
+
console.log('Servers:', servers)
|
|
35
|
+
|
|
36
|
+
// 列出所有 MCP 工具
|
|
37
|
+
console.log('\n--- MCP Tools ---')
|
|
38
|
+
const tools = framework.getTools().filter(t => t.name.startsWith('mcp_'))
|
|
39
|
+
console.log('MCP tools:', tools.map(t => t.name))
|
|
40
|
+
|
|
41
|
+
// 如果有配置的服务器,可以这样调用:
|
|
42
|
+
// const result = await framework.executeTool('mcp_call', {
|
|
43
|
+
// server: 'example',
|
|
44
|
+
// tool: 'tool_name',
|
|
45
|
+
// args: { /* tool arguments */ }
|
|
46
|
+
// })
|
|
47
|
+
|
|
48
|
+
// 清理
|
|
49
|
+
await framework.destroy()
|
|
50
|
+
console.log('\n[Done]')
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
main().catch(console.error)
|
|
@@ -1,49 +1,49 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Skill 管理器示例
|
|
3
|
-
* 展示如何加载和使用 Skill
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const { Framework } = require('../src')
|
|
7
|
-
const { SkillManagerPlugin } = require('../src/capabilities/skill-manager')
|
|
8
|
-
|
|
9
|
-
async function main() {
|
|
10
|
-
console.log('=== Skill Manager Example ===\n')
|
|
11
|
-
|
|
12
|
-
// 创建框架
|
|
13
|
-
const framework = new Framework({ debug: true })
|
|
14
|
-
|
|
15
|
-
// 创建 skills 目录(如果没有)
|
|
16
|
-
const fs = require('fs')
|
|
17
|
-
const skillsDir = './skills'
|
|
18
|
-
if (!fs.existsSync(skillsDir)) {
|
|
19
|
-
fs.mkdirSync(skillsDir, { recursive: true })
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// 加载 Skill 管理器插件
|
|
23
|
-
const skillPlugin = new SkillManagerPlugin({
|
|
24
|
-
skillsDir: skillsDir
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
await framework.loadPlugin(skillPlugin)
|
|
28
|
-
|
|
29
|
-
// 列出所有加载的 skills
|
|
30
|
-
console.log('\n--- Loaded Skills ---')
|
|
31
|
-
const skills = skillPlugin.getAllSkills()
|
|
32
|
-
console.log(`Found ${skills.length} skills:`)
|
|
33
|
-
for (const skill of skills) {
|
|
34
|
-
console.log(` - ${skill.name}: ${skill.metadata.description}`)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// 获取单个 skill
|
|
38
|
-
if (skillPlugin.hasSkill('hello-skill')) {
|
|
39
|
-
console.log('\n--- Hello Skill ---')
|
|
40
|
-
const helloSkill = skillPlugin.getSkill('hello-skill')
|
|
41
|
-
console.log('Content preview:', helloSkill.content.substring(0, 100) + '...')
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// 清理
|
|
45
|
-
await framework.destroy()
|
|
46
|
-
console.log('\n[Done]')
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
main().catch(console.error)
|
|
1
|
+
/**
|
|
2
|
+
* Skill 管理器示例
|
|
3
|
+
* 展示如何加载和使用 Skill
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { Framework } = require('../src')
|
|
7
|
+
const { SkillManagerPlugin } = require('../src/capabilities/skill-manager')
|
|
8
|
+
|
|
9
|
+
async function main() {
|
|
10
|
+
console.log('=== Skill Manager Example ===\n')
|
|
11
|
+
|
|
12
|
+
// 创建框架
|
|
13
|
+
const framework = new Framework({ debug: true })
|
|
14
|
+
|
|
15
|
+
// 创建 skills 目录(如果没有)
|
|
16
|
+
const fs = require('fs')
|
|
17
|
+
const skillsDir = './skills'
|
|
18
|
+
if (!fs.existsSync(skillsDir)) {
|
|
19
|
+
fs.mkdirSync(skillsDir, { recursive: true })
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// 加载 Skill 管理器插件
|
|
23
|
+
const skillPlugin = new SkillManagerPlugin({
|
|
24
|
+
skillsDir: skillsDir
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
await framework.loadPlugin(skillPlugin)
|
|
28
|
+
|
|
29
|
+
// 列出所有加载的 skills
|
|
30
|
+
console.log('\n--- Loaded Skills ---')
|
|
31
|
+
const skills = skillPlugin.getAllSkills()
|
|
32
|
+
console.log(`Found ${skills.length} skills:`)
|
|
33
|
+
for (const skill of skills) {
|
|
34
|
+
console.log(` - ${skill.name}: ${skill.metadata.description}`)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// 获取单个 skill
|
|
38
|
+
if (skillPlugin.hasSkill('hello-skill')) {
|
|
39
|
+
console.log('\n--- Hello Skill ---')
|
|
40
|
+
const helloSkill = skillPlugin.getSkill('hello-skill')
|
|
41
|
+
console.log('Content preview:', helloSkill.content.substring(0, 100) + '...')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// 清理
|
|
45
|
+
await framework.destroy()
|
|
46
|
+
console.log('\n[Done]')
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
main().catch(console.error)
|
package/examples/test-mcp.js
CHANGED
|
@@ -1,79 +1,79 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MCP 插件测试脚本
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
const { Framework } = require('../src')
|
|
6
|
-
const { MCPExecutorPlugin } = require('../src/executors/mcp-executor')
|
|
7
|
-
|
|
8
|
-
async function test() {
|
|
9
|
-
console.log('=== MCP Plugin Test ===\n')
|
|
10
|
-
|
|
11
|
-
console.log('1. Creating framework...')
|
|
12
|
-
const framework = new Framework({ debug: true })
|
|
13
|
-
|
|
14
|
-
console.log('2. Creating MCP plugin with fetch server...')
|
|
15
|
-
const mcpPlugin = new MCPExecutorPlugin({
|
|
16
|
-
servers: [
|
|
17
|
-
{
|
|
18
|
-
name: 'fetch',
|
|
19
|
-
command: 'uvx',
|
|
20
|
-
args: ['mcp-server-fetch']
|
|
21
|
-
}
|
|
22
|
-
]
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
console.log('3. Loading MCP plugin...')
|
|
26
|
-
await framework.loadPlugin(mcpPlugin)
|
|
27
|
-
|
|
28
|
-
console.log('4. Starting framework...')
|
|
29
|
-
await framework.pluginManager.startAll()
|
|
30
|
-
|
|
31
|
-
// 等待 MCP 服务器连接
|
|
32
|
-
console.log('\n5. Waiting for MCP connection...')
|
|
33
|
-
await new Promise(resolve => setTimeout(resolve, 2000))
|
|
34
|
-
|
|
35
|
-
console.log('\n6. Listing MCP servers...')
|
|
36
|
-
const servers = mcpPlugin.getServers()
|
|
37
|
-
console.log('Servers:', JSON.stringify(servers, null, 2))
|
|
38
|
-
|
|
39
|
-
console.log('\n7. Listing MCP tools...')
|
|
40
|
-
const mcpTools = framework.getTools().filter(t => t.name.startsWith('mcp_'))
|
|
41
|
-
console.log('MCP Tools:', mcpTools.map(t => t.name))
|
|
42
|
-
|
|
43
|
-
if (mcpTools.length > 0) {
|
|
44
|
-
console.log('\n8. Testing mcp_list_servers...')
|
|
45
|
-
const listResult = await framework.executeTool('mcp_list_servers', {})
|
|
46
|
-
console.log('List Servers Result:', JSON.stringify(listResult, null, 2))
|
|
47
|
-
|
|
48
|
-
console.log('\n9. Testing mcp_tool_schema...')
|
|
49
|
-
const schemaResult = await framework.executeTool('mcp_tool_schema', {
|
|
50
|
-
server: 'fetch',
|
|
51
|
-
tool: 'fetch'
|
|
52
|
-
})
|
|
53
|
-
console.log('Tool Schema:', JSON.stringify(schemaResult, null, 2))
|
|
54
|
-
|
|
55
|
-
console.log('\n10. Testing mcp_call (fetch a webpage)...')
|
|
56
|
-
const callResult = await framework.executeTool('mcp_call', {
|
|
57
|
-
server: 'fetch',
|
|
58
|
-
tool: 'fetch',
|
|
59
|
-
args_json: JSON.stringify({ url: 'https://httpbin.org/get' })
|
|
60
|
-
})
|
|
61
|
-
console.log('Fetch Result (truncated):', JSON.stringify(callResult, null, 2).substring(0, 500) + '...')
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
console.log('\n11. Destroying framework...')
|
|
65
|
-
await framework.destroy()
|
|
66
|
-
|
|
67
|
-
console.log('\n=== Test Complete ===')
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
test()
|
|
71
|
-
.then(() => {
|
|
72
|
-
console.log('\n✓ Test completed successfully')
|
|
73
|
-
process.exit(0)
|
|
74
|
-
})
|
|
75
|
-
.catch(err => {
|
|
76
|
-
console.error('\n✗ Test failed:', err.message)
|
|
77
|
-
console.error(err.stack)
|
|
78
|
-
process.exit(1)
|
|
79
|
-
})
|
|
1
|
+
/**
|
|
2
|
+
* MCP 插件测试脚本
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const { Framework } = require('../src')
|
|
6
|
+
const { MCPExecutorPlugin } = require('../src/executors/mcp-executor')
|
|
7
|
+
|
|
8
|
+
async function test() {
|
|
9
|
+
console.log('=== MCP Plugin Test ===\n')
|
|
10
|
+
|
|
11
|
+
console.log('1. Creating framework...')
|
|
12
|
+
const framework = new Framework({ debug: true })
|
|
13
|
+
|
|
14
|
+
console.log('2. Creating MCP plugin with fetch server...')
|
|
15
|
+
const mcpPlugin = new MCPExecutorPlugin({
|
|
16
|
+
servers: [
|
|
17
|
+
{
|
|
18
|
+
name: 'fetch',
|
|
19
|
+
command: 'uvx',
|
|
20
|
+
args: ['mcp-server-fetch']
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
console.log('3. Loading MCP plugin...')
|
|
26
|
+
await framework.loadPlugin(mcpPlugin)
|
|
27
|
+
|
|
28
|
+
console.log('4. Starting framework...')
|
|
29
|
+
await framework.pluginManager.startAll()
|
|
30
|
+
|
|
31
|
+
// 等待 MCP 服务器连接
|
|
32
|
+
console.log('\n5. Waiting for MCP connection...')
|
|
33
|
+
await new Promise(resolve => setTimeout(resolve, 2000))
|
|
34
|
+
|
|
35
|
+
console.log('\n6. Listing MCP servers...')
|
|
36
|
+
const servers = mcpPlugin.getServers()
|
|
37
|
+
console.log('Servers:', JSON.stringify(servers, null, 2))
|
|
38
|
+
|
|
39
|
+
console.log('\n7. Listing MCP tools...')
|
|
40
|
+
const mcpTools = framework.getTools().filter(t => t.name.startsWith('mcp_'))
|
|
41
|
+
console.log('MCP Tools:', mcpTools.map(t => t.name))
|
|
42
|
+
|
|
43
|
+
if (mcpTools.length > 0) {
|
|
44
|
+
console.log('\n8. Testing mcp_list_servers...')
|
|
45
|
+
const listResult = await framework.executeTool('mcp_list_servers', {})
|
|
46
|
+
console.log('List Servers Result:', JSON.stringify(listResult, null, 2))
|
|
47
|
+
|
|
48
|
+
console.log('\n9. Testing mcp_tool_schema...')
|
|
49
|
+
const schemaResult = await framework.executeTool('mcp_tool_schema', {
|
|
50
|
+
server: 'fetch',
|
|
51
|
+
tool: 'fetch'
|
|
52
|
+
})
|
|
53
|
+
console.log('Tool Schema:', JSON.stringify(schemaResult, null, 2))
|
|
54
|
+
|
|
55
|
+
console.log('\n10. Testing mcp_call (fetch a webpage)...')
|
|
56
|
+
const callResult = await framework.executeTool('mcp_call', {
|
|
57
|
+
server: 'fetch',
|
|
58
|
+
tool: 'fetch',
|
|
59
|
+
args_json: JSON.stringify({ url: 'https://httpbin.org/get' })
|
|
60
|
+
})
|
|
61
|
+
console.log('Fetch Result (truncated):', JSON.stringify(callResult, null, 2).substring(0, 500) + '...')
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
console.log('\n11. Destroying framework...')
|
|
65
|
+
await framework.destroy()
|
|
66
|
+
|
|
67
|
+
console.log('\n=== Test Complete ===')
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
test()
|
|
71
|
+
.then(() => {
|
|
72
|
+
console.log('\n✓ Test completed successfully')
|
|
73
|
+
process.exit(0)
|
|
74
|
+
})
|
|
75
|
+
.catch(err => {
|
|
76
|
+
console.error('\n✗ Test failed:', err.message)
|
|
77
|
+
console.error(err.stack)
|
|
78
|
+
process.exit(1)
|
|
79
|
+
})
|
package/examples/test-reload.js
CHANGED
|
@@ -1,61 +1,61 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MCP mcp_reload 功能测试
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
const { Framework } = require('../src')
|
|
6
|
-
const { MCPExecutorPlugin } = require('../src/executors/mcp-executor')
|
|
7
|
-
const fs = require('fs')
|
|
8
|
-
const path = require('path')
|
|
9
|
-
|
|
10
|
-
async function test() {
|
|
11
|
-
console.log('=== Testing mcp_reload ===\n')
|
|
12
|
-
|
|
13
|
-
// 创建框架
|
|
14
|
-
const framework = new Framework({ debug: true })
|
|
15
|
-
|
|
16
|
-
// 创建 MCP 插件
|
|
17
|
-
const mcpPlugin = new MCPExecutorPlugin({
|
|
18
|
-
servers: [
|
|
19
|
-
{ name: 'fetch', command: 'uvx', args: ['mcp-server-fetch'] }
|
|
20
|
-
]
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
// 加载并启动
|
|
24
|
-
await framework.loadPlugin(mcpPlugin)
|
|
25
|
-
await framework.pluginManager.startAll()
|
|
26
|
-
|
|
27
|
-
// 等待连接
|
|
28
|
-
await new Promise(r => setTimeout(r, 2000))
|
|
29
|
-
|
|
30
|
-
console.log('\n1. 初始服务器列表:')
|
|
31
|
-
const initialServers = mcpPlugin.getServers()
|
|
32
|
-
console.log(JSON.stringify(initialServers, null, 2))
|
|
33
|
-
|
|
34
|
-
// 测试 mcp_reload
|
|
35
|
-
console.log('\n2. 调用 mcp_reload...')
|
|
36
|
-
const result = await framework.executeTool('mcp_reload', {})
|
|
37
|
-
console.log('mcp_reload 结果:')
|
|
38
|
-
console.log(JSON.stringify(result, null, 2))
|
|
39
|
-
|
|
40
|
-
// 检查配置
|
|
41
|
-
console.log('\n3. 当前配置文件:')
|
|
42
|
-
const configPath = path.resolve('.agent/mcp_config.json')
|
|
43
|
-
if (fs.existsSync(configPath)) {
|
|
44
|
-
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'))
|
|
45
|
-
console.log(JSON.stringify(config, null, 2))
|
|
46
|
-
} else {
|
|
47
|
-
console.log('配置文件不存在: ' + configPath)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// 清理
|
|
51
|
-
await framework.destroy()
|
|
52
|
-
|
|
53
|
-
console.log('\n=== 测试完成 ===')
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
test()
|
|
57
|
-
.then(() => process.exit(0))
|
|
58
|
-
.catch(err => {
|
|
59
|
-
console.error('测试失败:', err)
|
|
60
|
-
process.exit(1)
|
|
61
|
-
})
|
|
1
|
+
/**
|
|
2
|
+
* MCP mcp_reload 功能测试
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const { Framework } = require('../src')
|
|
6
|
+
const { MCPExecutorPlugin } = require('../src/executors/mcp-executor')
|
|
7
|
+
const fs = require('fs')
|
|
8
|
+
const path = require('path')
|
|
9
|
+
|
|
10
|
+
async function test() {
|
|
11
|
+
console.log('=== Testing mcp_reload ===\n')
|
|
12
|
+
|
|
13
|
+
// 创建框架
|
|
14
|
+
const framework = new Framework({ debug: true })
|
|
15
|
+
|
|
16
|
+
// 创建 MCP 插件
|
|
17
|
+
const mcpPlugin = new MCPExecutorPlugin({
|
|
18
|
+
servers: [
|
|
19
|
+
{ name: 'fetch', command: 'uvx', args: ['mcp-server-fetch'] }
|
|
20
|
+
]
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
// 加载并启动
|
|
24
|
+
await framework.loadPlugin(mcpPlugin)
|
|
25
|
+
await framework.pluginManager.startAll()
|
|
26
|
+
|
|
27
|
+
// 等待连接
|
|
28
|
+
await new Promise(r => setTimeout(r, 2000))
|
|
29
|
+
|
|
30
|
+
console.log('\n1. 初始服务器列表:')
|
|
31
|
+
const initialServers = mcpPlugin.getServers()
|
|
32
|
+
console.log(JSON.stringify(initialServers, null, 2))
|
|
33
|
+
|
|
34
|
+
// 测试 mcp_reload
|
|
35
|
+
console.log('\n2. 调用 mcp_reload...')
|
|
36
|
+
const result = await framework.executeTool('mcp_reload', {})
|
|
37
|
+
console.log('mcp_reload 结果:')
|
|
38
|
+
console.log(JSON.stringify(result, null, 2))
|
|
39
|
+
|
|
40
|
+
// 检查配置
|
|
41
|
+
console.log('\n3. 当前配置文件:')
|
|
42
|
+
const configPath = path.resolve('.agent/mcp_config.json')
|
|
43
|
+
if (fs.existsSync(configPath)) {
|
|
44
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'))
|
|
45
|
+
console.log(JSON.stringify(config, null, 2))
|
|
46
|
+
} else {
|
|
47
|
+
console.log('配置文件不存在: ' + configPath)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// 清理
|
|
51
|
+
await framework.destroy()
|
|
52
|
+
|
|
53
|
+
console.log('\n=== 测试完成 ===')
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
test()
|
|
57
|
+
.then(() => process.exit(0))
|
|
58
|
+
.catch(err => {
|
|
59
|
+
console.error('测试失败:', err)
|
|
60
|
+
process.exit(1)
|
|
61
|
+
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "foliko",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.51",
|
|
4
4
|
"description": "简约的插件化 Agent 框架",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"node-telegram-bot-api": "^0.67.0",
|
|
41
41
|
"nodemailer": "^6.10.0",
|
|
42
42
|
"qrcode-terminal": "^0.12.0",
|
|
43
|
+
"tiktoken": "^1.0.22",
|
|
43
44
|
"zod": "^3.24.0"
|
|
44
45
|
}
|
|
45
46
|
}
|
|
@@ -32,7 +32,7 @@ class SessionPlugin extends Plugin {
|
|
|
32
32
|
this.config = {
|
|
33
33
|
sessionTTL: config.sessionTTL || 30 * 60 * 1000, // 30分钟
|
|
34
34
|
maxSessions: config.maxSessions || 100,
|
|
35
|
-
maxHistoryLength: config.maxHistoryLength ||
|
|
35
|
+
maxHistoryLength: config.maxHistoryLength || 150, // 放宽到 150,Agent 已有智能压缩
|
|
36
36
|
autoCleanup: config.autoCleanup !== false,
|
|
37
37
|
cleanupInterval: config.cleanupInterval || 5 * 60 * 1000 // 5分钟
|
|
38
38
|
}
|
package/src/core/agent-chat.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
const { EventEmitter } = require('../utils/event-emitter')
|
|
7
|
+
const tiktoken = require('tiktoken')
|
|
7
8
|
|
|
8
9
|
class AgentChatHandler extends EventEmitter {
|
|
9
10
|
/**
|
|
@@ -27,8 +28,166 @@ class AgentChatHandler extends EventEmitter {
|
|
|
27
28
|
this._tools = new Map()
|
|
28
29
|
this._maxSteps = 20
|
|
29
30
|
|
|
30
|
-
//
|
|
31
|
-
this.
|
|
31
|
+
// 上下文压缩配置
|
|
32
|
+
this._maxContextTokens = config.maxContextTokens || 100000
|
|
33
|
+
this._compressionThreshold = config.compressionThreshold || 0.75
|
|
34
|
+
this._keepRecentMessages = config.keepRecentMessages || 30
|
|
35
|
+
this._enableSmartCompress = config.enableSmartCompress !== false // 默认开启智能摘要
|
|
36
|
+
this._encoder = null
|
|
37
|
+
this._compressionCount = 0 // 压缩次数统计
|
|
38
|
+
|
|
39
|
+
// 初始化编码器
|
|
40
|
+
this._initEncoder()
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 初始化 tiktoken 编码器
|
|
45
|
+
* @private
|
|
46
|
+
*/
|
|
47
|
+
_initEncoder() {
|
|
48
|
+
try {
|
|
49
|
+
// cl100k_base 是 GPT-4/ChatGPT 使用的编码
|
|
50
|
+
this._encoder = tiktoken.get_encoding('cl100k_base')
|
|
51
|
+
} catch (err) {
|
|
52
|
+
console.warn('[AgentChat] Failed to initialize tiktoken encoder:', err.message)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 计算文本的 token 数
|
|
58
|
+
* @param {string} text
|
|
59
|
+
* @returns {number}
|
|
60
|
+
* @private
|
|
61
|
+
*/
|
|
62
|
+
_countTokens(text) {
|
|
63
|
+
if (!this._encoder || !text) return 0
|
|
64
|
+
try {
|
|
65
|
+
return this._encoder.encode(text).length
|
|
66
|
+
} catch (err) {
|
|
67
|
+
// 粗略估算:约 4 字符 = 1 token
|
|
68
|
+
return Math.ceil(text.length / 4)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* 计算消息列表的总 token 数
|
|
74
|
+
* @param {Array} messages
|
|
75
|
+
* @returns {number}
|
|
76
|
+
* @private
|
|
77
|
+
*/
|
|
78
|
+
_countMessagesTokens(messages) {
|
|
79
|
+
let total = 0
|
|
80
|
+
for (const msg of messages) {
|
|
81
|
+
total += 4 // role 标记
|
|
82
|
+
if (typeof msg.content === 'string') {
|
|
83
|
+
total += this._countTokens(msg.content)
|
|
84
|
+
} else if (Array.isArray(msg.content)) {
|
|
85
|
+
for (const part of msg.content) {
|
|
86
|
+
if (part.text) {
|
|
87
|
+
total += this._countTokens(part.text)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
total += 4 // 结尾标记
|
|
93
|
+
return total
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* 检查是否需要压缩上下文
|
|
98
|
+
* @returns {boolean}
|
|
99
|
+
* @private
|
|
100
|
+
*/
|
|
101
|
+
_shouldCompress() {
|
|
102
|
+
const totalTokens = this._countMessagesTokens(this._messages)
|
|
103
|
+
return totalTokens > this._maxContextTokens * this._compressionThreshold
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* 压缩上下文消息(智能摘要模式)
|
|
108
|
+
* 策略:
|
|
109
|
+
* 1. 如果启用了智能摘要且有 AI 客户端,对早期消息进行 AI 总结
|
|
110
|
+
* 2. 否则使用简单裁剪 + 标记
|
|
111
|
+
* @private
|
|
112
|
+
*/
|
|
113
|
+
async _compressContext() {
|
|
114
|
+
if (this._messages.length <= this._keepRecentMessages) {
|
|
115
|
+
return
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const systemMessages = this._messages.filter(m => m.role === 'system')
|
|
119
|
+
const otherMessages = this._messages.filter(m => m.role !== 'system')
|
|
120
|
+
|
|
121
|
+
// 保留最近的 N 条非系统消息
|
|
122
|
+
const recentMessages = otherMessages.slice(-this._keepRecentMessages)
|
|
123
|
+
const messagesToSummarize = otherMessages.slice(0, -this._keepRecentMessages)
|
|
124
|
+
|
|
125
|
+
const compressedCount = messagesToSummarize.length
|
|
126
|
+
let summaryContent = ''
|
|
127
|
+
|
|
128
|
+
// 尝试使用 AI 总结
|
|
129
|
+
if (this._enableSmartCompress && this._aiClient) {
|
|
130
|
+
try {
|
|
131
|
+
const summaryText = await this._summarizeMessages(messagesToSummarize)
|
|
132
|
+
summaryContent = `[早期对话摘要]: ${summaryText}`
|
|
133
|
+
console.log(`[AgentChat] AI 摘要生成成功 (${summaryText.length} chars)`)
|
|
134
|
+
} catch (err) {
|
|
135
|
+
console.warn('[AgentChat] AI 摘要失败,使用简单压缩:', err.message)
|
|
136
|
+
summaryContent = `[上下文已压缩: 省略了 ${compressedCount} 条早期消息。保留了最近 ${this._keepRecentMessages} 条对话记录。]`
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
summaryContent = `[上下文已压缩: 省略了 ${compressedCount} 条早期消息。保留了最近 ${this._keepRecentMessages} 条对话记录。]`
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const summary = {
|
|
143
|
+
role: 'system',
|
|
144
|
+
content: summaryContent
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
this._messages = [...systemMessages, summary, ...recentMessages]
|
|
148
|
+
this._compressionCount++
|
|
149
|
+
|
|
150
|
+
const totalTokens = this._countMessagesTokens(this._messages)
|
|
151
|
+
console.log(`[AgentChat] Context compressed (${this._compressionCount} times). Messages: ${this._messages.length}, Est. tokens: ${totalTokens}`)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* 使用 AI 对消息进行总结
|
|
156
|
+
* @param {Array} messages - 要总结的消息
|
|
157
|
+
* @returns {Promise<string>} 总结文本
|
|
158
|
+
* @private
|
|
159
|
+
*/
|
|
160
|
+
async _summarizeMessages(messages) {
|
|
161
|
+
if (!this._aiClient || messages.length === 0) {
|
|
162
|
+
return '(无早期对话)'
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// 构建总结提示
|
|
166
|
+
const conversationText = messages.map(m => {
|
|
167
|
+
const role = m.role === 'user' ? '用户' : '助手'
|
|
168
|
+
const content = typeof m.content === 'string' ? m.content : JSON.stringify(m.content)
|
|
169
|
+
return `${role}: ${content}`
|
|
170
|
+
}).join('\n')
|
|
171
|
+
|
|
172
|
+
const summarizePrompt = `请简洁地总结以下对话的要点,保留关键信息和用户需求:
|
|
173
|
+
|
|
174
|
+
${conversationText}
|
|
175
|
+
|
|
176
|
+
总结要求:
|
|
177
|
+
1. 提取用户的主要需求和意图
|
|
178
|
+
2. 保留关键的技术细节或决策
|
|
179
|
+
3. 不要超过 1000 字
|
|
180
|
+
4. 用中文回复`
|
|
181
|
+
|
|
182
|
+
// 直接使用已配置的 AI 客户端生成摘要(复用主 Agent 的配置)
|
|
183
|
+
const result = await this._aiClient.generate({
|
|
184
|
+
messages: [
|
|
185
|
+
{ role: 'user', content: summarizePrompt }
|
|
186
|
+
],
|
|
187
|
+
...this.providerOptions
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
return result.text || '(总结生成失败)'
|
|
32
191
|
}
|
|
33
192
|
|
|
34
193
|
/**
|
|
@@ -63,6 +222,7 @@ class AgentChatHandler extends EventEmitter {
|
|
|
63
222
|
*/
|
|
64
223
|
clearHistory() {
|
|
65
224
|
this._messages = []
|
|
225
|
+
this._compressionCount = 0
|
|
66
226
|
return this
|
|
67
227
|
}
|
|
68
228
|
|
|
@@ -108,6 +268,11 @@ class AgentChatHandler extends EventEmitter {
|
|
|
108
268
|
|
|
109
269
|
this._messages.push(userMessage)
|
|
110
270
|
|
|
271
|
+
// 检查是否需要压缩上下文
|
|
272
|
+
if (this._shouldCompress()) {
|
|
273
|
+
await this._compressContext()
|
|
274
|
+
}
|
|
275
|
+
|
|
111
276
|
const maxSteps = options.maxSteps || this._maxSteps
|
|
112
277
|
const tools = this._getAITools(tool)
|
|
113
278
|
|
|
@@ -169,6 +334,14 @@ class AgentChatHandler extends EventEmitter {
|
|
|
169
334
|
|
|
170
335
|
this._messages.push(userMessage)
|
|
171
336
|
|
|
337
|
+
// 检查是否需要压缩上下文(同步调用,不阻塞流式输出)
|
|
338
|
+
if (this._shouldCompress()) {
|
|
339
|
+
// 压缩在后台进行,不等待完成,避免阻塞流式输出
|
|
340
|
+
this._compressContext().catch(err => {
|
|
341
|
+
console.warn('[AgentChat] Background compression failed:', err.message)
|
|
342
|
+
})
|
|
343
|
+
}
|
|
344
|
+
|
|
172
345
|
const maxSteps = options.maxSteps || this._maxSteps
|
|
173
346
|
const tools = this._getAITools(tool)
|
|
174
347
|
|
|
@@ -301,6 +474,10 @@ class AgentChatHandler extends EventEmitter {
|
|
|
301
474
|
destroy() {
|
|
302
475
|
this._messages = []
|
|
303
476
|
this._tools.clear()
|
|
477
|
+
if (this._encoder) {
|
|
478
|
+
this._encoder.free()
|
|
479
|
+
this._encoder = null
|
|
480
|
+
}
|
|
304
481
|
this.removeAllListeners()
|
|
305
482
|
}
|
|
306
483
|
}
|
package/src/core/agent.js
CHANGED
|
@@ -270,7 +270,12 @@ class Agent extends EventEmitter {
|
|
|
270
270
|
provider: this.provider,
|
|
271
271
|
apiKey: this.apiKey,
|
|
272
272
|
baseURL: this.baseURL,
|
|
273
|
-
providerOptions: this.providerOptions
|
|
273
|
+
providerOptions: this.providerOptions,
|
|
274
|
+
// 上下文压缩配置
|
|
275
|
+
maxContextTokens: this.config.maxContextTokens,
|
|
276
|
+
compressionThreshold: this.config.compressionThreshold,
|
|
277
|
+
keepRecentMessages: this.config.keepRecentMessages,
|
|
278
|
+
enableSmartCompress: this.config.enableSmartCompress
|
|
274
279
|
})
|
|
275
280
|
|
|
276
281
|
if (aiClient) {
|
|
@@ -1,58 +1,58 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Executor 基类
|
|
3
|
-
* 执行器的基类,定义执行器接口
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const { EventEmitter } = require('../utils/event-emitter')
|
|
7
|
-
|
|
8
|
-
class ExecutorBase extends EventEmitter {
|
|
9
|
-
/**
|
|
10
|
-
* @param {string} name - 执行器名称
|
|
11
|
-
*/
|
|
12
|
-
constructor(name) {
|
|
13
|
-
super()
|
|
14
|
-
this.name = name
|
|
15
|
-
this._enabled = true
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* 执行
|
|
20
|
-
* @param {Object} params - 执行参数
|
|
21
|
-
* @returns {Promise<any>}
|
|
22
|
-
*/
|
|
23
|
-
async execute(params) {
|
|
24
|
-
throw new Error('execute() must be implemented')
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* 启用执行器
|
|
29
|
-
*/
|
|
30
|
-
enable() {
|
|
31
|
-
this._enabled = true
|
|
32
|
-
return this
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* 禁用执行器
|
|
37
|
-
*/
|
|
38
|
-
disable() {
|
|
39
|
-
this._enabled = false
|
|
40
|
-
return this
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* 是否启用
|
|
45
|
-
*/
|
|
46
|
-
isEnabled() {
|
|
47
|
-
return this._enabled
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* 销毁
|
|
52
|
-
*/
|
|
53
|
-
destroy() {
|
|
54
|
-
this.removeAllListeners()
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
module.exports = { ExecutorBase }
|
|
1
|
+
/**
|
|
2
|
+
* Executor 基类
|
|
3
|
+
* 执行器的基类,定义执行器接口
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { EventEmitter } = require('../utils/event-emitter')
|
|
7
|
+
|
|
8
|
+
class ExecutorBase extends EventEmitter {
|
|
9
|
+
/**
|
|
10
|
+
* @param {string} name - 执行器名称
|
|
11
|
+
*/
|
|
12
|
+
constructor(name) {
|
|
13
|
+
super()
|
|
14
|
+
this.name = name
|
|
15
|
+
this._enabled = true
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 执行
|
|
20
|
+
* @param {Object} params - 执行参数
|
|
21
|
+
* @returns {Promise<any>}
|
|
22
|
+
*/
|
|
23
|
+
async execute(params) {
|
|
24
|
+
throw new Error('execute() must be implemented')
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 启用执行器
|
|
29
|
+
*/
|
|
30
|
+
enable() {
|
|
31
|
+
this._enabled = true
|
|
32
|
+
return this
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 禁用执行器
|
|
37
|
+
*/
|
|
38
|
+
disable() {
|
|
39
|
+
this._enabled = false
|
|
40
|
+
return this
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 是否启用
|
|
45
|
+
*/
|
|
46
|
+
isEnabled() {
|
|
47
|
+
return this._enabled
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 销毁
|
|
52
|
+
*/
|
|
53
|
+
destroy() {
|
|
54
|
+
this.removeAllListeners()
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
module.exports = { ExecutorBase }
|
package/test-server.js
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
const { serve } = require('@hono/node-server');
|
|
2
|
-
const { Hono } = require('hono');
|
|
3
|
-
|
|
4
|
-
const app = new Hono();
|
|
5
|
-
|
|
6
|
-
// 简单路由
|
|
7
|
-
app.get('/test', (c) => {
|
|
8
|
-
console.log('Handler called');
|
|
9
|
-
return c.json({ message: 'Hello World' });
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
const server = serve({
|
|
13
|
-
fetch: app.fetch,
|
|
14
|
-
port: 3001
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
server.on('request', (req) => {
|
|
18
|
-
console.log('Request:', req.method, req.url);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
server.on('error', (err) => {
|
|
22
|
-
console.error('Server error:', err);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
console.log('Server started on port 3001');
|
|
1
|
+
const { serve } = require('@hono/node-server');
|
|
2
|
+
const { Hono } = require('hono');
|
|
3
|
+
|
|
4
|
+
const app = new Hono();
|
|
5
|
+
|
|
6
|
+
// 简单路由
|
|
7
|
+
app.get('/test', (c) => {
|
|
8
|
+
console.log('Handler called');
|
|
9
|
+
return c.json({ message: 'Hello World' });
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
const server = serve({
|
|
13
|
+
fetch: app.fetch,
|
|
14
|
+
port: 3001
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
server.on('request', (req) => {
|
|
18
|
+
console.log('Request:', req.method, req.url);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
server.on('error', (err) => {
|
|
22
|
+
console.error('Server error:', err);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
console.log('Server started on port 3001');
|
package/test.txt
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
Hello from static resource test!
|
|
2
|
-
This is a test file for verifying static resource serving.
|
|
3
|
-
Timestamp: 2026-03-24
|
|
1
|
+
Hello from static resource test!
|
|
2
|
+
This is a test file for verifying static resource serving.
|
|
3
|
+
Timestamp: 2026-03-24
|