foliko 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +30 -0
- package/22.txt +10 -0
- package/README.md +218 -0
- package/SPEC.md +452 -0
- package/cli/bin/foliko.js +12 -0
- package/cli/src/commands/chat.js +75 -0
- package/cli/src/index.js +64 -0
- package/cli/src/ui/chat-ui.js +272 -0
- package/cli/src/utils/ansi.js +40 -0
- package/cli/src/utils/markdown.js +296 -0
- package/docs/quick-reference.md +131 -0
- package/docs/user-manual.md +1205 -0
- package/examples/basic.js +110 -0
- package/examples/bootstrap.js +93 -0
- package/examples/mcp-example.js +53 -0
- package/examples/skill-example.js +49 -0
- package/examples/workflow.js +158 -0
- package/package.json +36 -0
- package/plugins/ai-plugin.js +89 -0
- package/plugins/audit-plugin.js +187 -0
- package/plugins/default-plugins.js +412 -0
- package/plugins/file-system-plugin.js +344 -0
- package/plugins/install-plugin.js +93 -0
- package/plugins/python-executor-plugin.js +331 -0
- package/plugins/rules-plugin.js +292 -0
- package/plugins/scheduler-plugin.js +426 -0
- package/plugins/session-plugin.js +343 -0
- package/plugins/shell-executor-plugin.js +196 -0
- package/plugins/storage-plugin.js +237 -0
- package/plugins/subagent-plugin.js +395 -0
- package/plugins/think-plugin.js +329 -0
- package/plugins/tools-plugin.js +114 -0
- package/skills/mcp-usage/SKILL.md +198 -0
- package/skills/vb-agent-dev/AGENTS.md +162 -0
- package/skills/vb-agent-dev/SKILL.md +370 -0
- package/src/capabilities/index.js +11 -0
- package/src/capabilities/skill-manager.js +319 -0
- package/src/capabilities/workflow-engine.js +401 -0
- package/src/core/agent-chat.js +311 -0
- package/src/core/agent.js +573 -0
- package/src/core/framework.js +255 -0
- package/src/core/index.js +19 -0
- package/src/core/plugin-base.js +205 -0
- package/src/core/plugin-manager.js +392 -0
- package/src/core/provider.js +108 -0
- package/src/core/tool-registry.js +134 -0
- package/src/core/tool-router.js +216 -0
- package/src/executors/executor-base.js +58 -0
- package/src/executors/mcp-executor.js +728 -0
- package/src/index.js +37 -0
- package/src/utils/event-emitter.js +97 -0
- package/test-chat.js +129 -0
- package/test-mcp.js +79 -0
- package/test-reload.js +61 -0
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PluginManager 插件管理器
|
|
3
|
+
* 负责插件的加载、卸载、重载
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { Plugin } = require('./plugin-base')
|
|
7
|
+
|
|
8
|
+
class PluginManager {
|
|
9
|
+
/**
|
|
10
|
+
* @param {Framework} framework - 框架实例
|
|
11
|
+
*/
|
|
12
|
+
constructor(framework) {
|
|
13
|
+
this.framework = framework
|
|
14
|
+
this._plugins = new Map()
|
|
15
|
+
this._loading = false
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 注册插件(不加载)
|
|
20
|
+
* @param {Plugin|Object} plugin - 插件实例或定义
|
|
21
|
+
*/
|
|
22
|
+
register(plugin) {
|
|
23
|
+
if (!plugin.name) {
|
|
24
|
+
throw new Error('Plugin must have a name')
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let pluginInstance = plugin
|
|
28
|
+
if (!(plugin instanceof Plugin)) {
|
|
29
|
+
// 从对象创建插件实例
|
|
30
|
+
pluginInstance = this._createFromObject(plugin)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
this._plugins.set(pluginInstance.name, {
|
|
34
|
+
instance: pluginInstance,
|
|
35
|
+
status: 'registered'
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
this.framework.emit('plugin:registered', pluginInstance)
|
|
39
|
+
return this
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 加载插件
|
|
44
|
+
* @param {Plugin|Object} plugin - 插件实例或定义
|
|
45
|
+
*/
|
|
46
|
+
async load(plugin) {
|
|
47
|
+
if (this._loading) {
|
|
48
|
+
throw new Error('Cannot load plugin during another load operation')
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
this._loading = true
|
|
52
|
+
try {
|
|
53
|
+
let pluginInstance = plugin
|
|
54
|
+
|
|
55
|
+
if (!(plugin instanceof Plugin)) {
|
|
56
|
+
pluginInstance = this._createFromObject(plugin)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 如果已注册,使用已注册的实例
|
|
60
|
+
const existing = this._plugins.get(pluginInstance.name)
|
|
61
|
+
if (existing) {
|
|
62
|
+
pluginInstance = existing.instance
|
|
63
|
+
|
|
64
|
+
// 如果已加载且已启动,直接返回
|
|
65
|
+
if (existing.status === 'loaded' && pluginInstance._started) {
|
|
66
|
+
console.warn(`[PluginManager] Plugin '${pluginInstance.name}' already loaded`)
|
|
67
|
+
return pluginInstance
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// 如果已加载但未启动(重载场景),直接启动
|
|
71
|
+
if (existing.status === 'loaded' && !pluginInstance._started) {
|
|
72
|
+
try {
|
|
73
|
+
if (typeof pluginInstance.start === 'function') {
|
|
74
|
+
await pluginInstance.start(this.framework)
|
|
75
|
+
pluginInstance._started = true
|
|
76
|
+
}
|
|
77
|
+
} catch (err) {
|
|
78
|
+
console.error(`[PluginManager] Start failed for '${pluginInstance.name}':`, err.message)
|
|
79
|
+
}
|
|
80
|
+
this.framework.emit('plugin:loaded', pluginInstance)
|
|
81
|
+
return pluginInstance
|
|
82
|
+
}
|
|
83
|
+
} else {
|
|
84
|
+
// 未注册,先注册
|
|
85
|
+
this.register(pluginInstance)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const entry = this._plugins.get(pluginInstance.name)
|
|
89
|
+
|
|
90
|
+
// 调用 install
|
|
91
|
+
try {
|
|
92
|
+
await entry.instance.install(this.framework)
|
|
93
|
+
} catch (err) {
|
|
94
|
+
console.error(`[PluginManager] Install failed for '${pluginInstance.name}':`, err.message)
|
|
95
|
+
throw err
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
entry.status = 'loaded'
|
|
99
|
+
|
|
100
|
+
// 如果处于 bootstrap 模式,由外部统一调用 startAll()
|
|
101
|
+
// 否则直接启动
|
|
102
|
+
if (this._bootstrapping) {
|
|
103
|
+
// bootstrap 模式下不启动,由 bootstrapDefaults 统一启动
|
|
104
|
+
} else {
|
|
105
|
+
// 非 bootstrap 模式下直接启动
|
|
106
|
+
try {
|
|
107
|
+
if (typeof pluginInstance.start === 'function') {
|
|
108
|
+
await pluginInstance.start(this.framework)
|
|
109
|
+
pluginInstance._started = true
|
|
110
|
+
}
|
|
111
|
+
} catch (err) {
|
|
112
|
+
console.error(`[PluginManager] Start failed for '${pluginInstance.name}':`, err.message)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
this.framework.emit('plugin:loaded', pluginInstance)
|
|
117
|
+
return pluginInstance
|
|
118
|
+
} finally {
|
|
119
|
+
this._loading = false
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* 设置 bootstrap 模式
|
|
125
|
+
*/
|
|
126
|
+
setBootstrapping(value) {
|
|
127
|
+
this._bootstrapping = value
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* 卸载插件
|
|
132
|
+
* @param {string} name - 插件名称
|
|
133
|
+
*/
|
|
134
|
+
async unload(name) {
|
|
135
|
+
const entry = this._plugins.get(name)
|
|
136
|
+
if (!entry) {
|
|
137
|
+
return false
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const { instance } = entry
|
|
141
|
+
|
|
142
|
+
// 调用 uninstall
|
|
143
|
+
try {
|
|
144
|
+
await instance.uninstall(this.framework)
|
|
145
|
+
} catch (err) {
|
|
146
|
+
console.error(`[PluginManager] Uninstall error for '${name}':`, err.message)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
entry.status = 'unloaded'
|
|
150
|
+
this.framework.emit('plugin:unloaded', instance)
|
|
151
|
+
return true
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* 重载插件
|
|
156
|
+
* @param {string} name - 插件名称
|
|
157
|
+
*/
|
|
158
|
+
async reload(name) {
|
|
159
|
+
const entry = this._plugins.get(name)
|
|
160
|
+
if (!entry) {
|
|
161
|
+
throw new Error(`Plugin '${name}' not found`)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const { instance } = entry
|
|
165
|
+
|
|
166
|
+
// 调用 reload
|
|
167
|
+
try {
|
|
168
|
+
await instance.reload(this.framework)
|
|
169
|
+
this.framework.emit('plugin:reloaded', instance)
|
|
170
|
+
} catch (err) {
|
|
171
|
+
console.error(`[PluginManager] Reload error for '${name}':`, err.message)
|
|
172
|
+
throw err
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return instance
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* 重载所有插件
|
|
180
|
+
*/
|
|
181
|
+
async reloadAll() {
|
|
182
|
+
// 1. 重置所有插件的启动标志
|
|
183
|
+
for (const entry of this._plugins.values()) {
|
|
184
|
+
entry.instance._started = false
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// 2. 扫描 .agent/plugins 目录,加载新插件
|
|
188
|
+
await this._discoverCustomPlugins()
|
|
189
|
+
|
|
190
|
+
// 3. 启动所有未启动的插件
|
|
191
|
+
await this.startAll()
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* 发现并加载自定义插件
|
|
196
|
+
* @private
|
|
197
|
+
*/
|
|
198
|
+
async _discoverCustomPlugins() {
|
|
199
|
+
const fs = require('fs')
|
|
200
|
+
const path = require('path')
|
|
201
|
+
|
|
202
|
+
const pluginsDir = path.resolve(process.cwd(), '.agent', 'plugins')
|
|
203
|
+
if (!fs.existsSync(pluginsDir)) {
|
|
204
|
+
return
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const files = fs.readdirSync(pluginsDir).filter(f => f.endsWith('.js'))
|
|
208
|
+
|
|
209
|
+
// 从 pluginsDir 推导 agentDir(pluginsDir = <agentDir>/plugins)
|
|
210
|
+
const agentDir = path.dirname(pluginsDir)
|
|
211
|
+
const agentNodeModules = path.join(agentDir, 'node_modules')
|
|
212
|
+
|
|
213
|
+
for (const file of files) {
|
|
214
|
+
try {
|
|
215
|
+
const pluginPath = path.join(pluginsDir, file)
|
|
216
|
+
|
|
217
|
+
// 添加模块路径到搜索路径(优先级从高到低)
|
|
218
|
+
const modulePathsToAdd = [
|
|
219
|
+
agentNodeModules, // .agent/node_modules(项目本地安装的包)
|
|
220
|
+
path.join(__dirname, '..', '..', 'node_modules') // 全局安装的包
|
|
221
|
+
]
|
|
222
|
+
for (const mp of modulePathsToAdd) {
|
|
223
|
+
if (fs.existsSync(mp) && !module.paths.includes(mp)) {
|
|
224
|
+
module.paths.unshift(mp)
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// 清除缓存
|
|
229
|
+
delete require.cache[require.resolve(pluginPath)]
|
|
230
|
+
const pluginModule = require(pluginPath)
|
|
231
|
+
|
|
232
|
+
let plugin
|
|
233
|
+
if (typeof pluginModule === 'function') {
|
|
234
|
+
plugin = pluginModule
|
|
235
|
+
} else if (pluginModule.default) {
|
|
236
|
+
plugin = pluginModule.default
|
|
237
|
+
} else {
|
|
238
|
+
plugin = pluginModule
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// 获取插件名称
|
|
242
|
+
let pluginName
|
|
243
|
+
try {
|
|
244
|
+
const tempPlugin = plugin.prototype instanceof require('./plugin-base')
|
|
245
|
+
? new plugin() : (typeof plugin === 'function' ? plugin() : plugin)
|
|
246
|
+
pluginName = tempPlugin.name || file.replace('.js', '')
|
|
247
|
+
} catch {
|
|
248
|
+
pluginName = file.replace('.js', '')
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// 如果插件已加载且已启动,跳过
|
|
252
|
+
if (this.has(pluginName) && this.get(pluginName)?._started) {
|
|
253
|
+
continue
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
console.log(`[PluginManager] Loading new plugin: ${file}`)
|
|
257
|
+
await this.load(plugin)
|
|
258
|
+
} catch (err) {
|
|
259
|
+
console.error(`[PluginManager] Failed to load plugin ${file}:`, err.message)
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* 获取插件
|
|
266
|
+
* @param {string} name - 插件名称
|
|
267
|
+
*/
|
|
268
|
+
get(name) {
|
|
269
|
+
return this._plugins.get(name)?.instance
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* 获取所有已加载插件
|
|
274
|
+
*/
|
|
275
|
+
getAll() {
|
|
276
|
+
return Array.from(this._plugins.values())
|
|
277
|
+
.filter(e => e.status === 'loaded')
|
|
278
|
+
.map(e => ({ name: e.instance.name, instance: e.instance }))
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* 检查插件是否存在
|
|
283
|
+
* @param {string} name - 插件名称
|
|
284
|
+
*/
|
|
285
|
+
has(name) {
|
|
286
|
+
return this._plugins.has(name)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* 检查插件是否已加载
|
|
291
|
+
* @param {string} name - 插件名称
|
|
292
|
+
*/
|
|
293
|
+
isLoaded(name) {
|
|
294
|
+
return this._plugins.get(name)?.status === 'loaded'
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* 启动所有已加载但未启动的插件(按优先级排序)
|
|
299
|
+
*/
|
|
300
|
+
async startAll() {
|
|
301
|
+
const entries = Array.from(this._plugins.values())
|
|
302
|
+
.filter(e => e.status === 'loaded')
|
|
303
|
+
.sort((a, b) => (a.instance.priority || 100) - (b.instance.priority || 100))
|
|
304
|
+
|
|
305
|
+
for (const entry of entries) {
|
|
306
|
+
const instance = entry.instance
|
|
307
|
+
// 跳过已经启动过的插件
|
|
308
|
+
if (instance._started) {
|
|
309
|
+
continue
|
|
310
|
+
}
|
|
311
|
+
try {
|
|
312
|
+
if (typeof instance.start === 'function') {
|
|
313
|
+
await instance.start(this.framework)
|
|
314
|
+
instance._started = true
|
|
315
|
+
}
|
|
316
|
+
} catch (err) {
|
|
317
|
+
console.error(`[PluginManager] Start failed for '${instance.name}':`, err.message)
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* 启动所有已加载插件
|
|
324
|
+
* @private
|
|
325
|
+
*/
|
|
326
|
+
async _startAll() {
|
|
327
|
+
const loaded = this.getAll()
|
|
328
|
+
.sort((a, b) => (a.instance.priority || 100) - (b.instance.priority || 100))
|
|
329
|
+
|
|
330
|
+
for (const { instance } of loaded) {
|
|
331
|
+
try {
|
|
332
|
+
if (typeof instance.start === 'function') {
|
|
333
|
+
await instance.start(this.framework)
|
|
334
|
+
}
|
|
335
|
+
} catch (err) {
|
|
336
|
+
console.error(`[PluginManager] Start failed for '${instance.name}':`, err.message)
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* 从对象创建插件实例
|
|
343
|
+
* @private
|
|
344
|
+
*/
|
|
345
|
+
_createFromObject(obj) {
|
|
346
|
+
const { Plugin } = require('./plugin-base')
|
|
347
|
+
|
|
348
|
+
// 如果是类(构造函数),直接实例化
|
|
349
|
+
if (typeof obj === 'function') {
|
|
350
|
+
// 检查是否是 Plugin 的子类(通过 prototype chain)
|
|
351
|
+
if (obj.prototype instanceof Plugin) {
|
|
352
|
+
return new obj()
|
|
353
|
+
}
|
|
354
|
+
// 否则是工厂函数,调用它获取类或实例
|
|
355
|
+
const result = obj(Plugin)
|
|
356
|
+
// 递归处理返回值
|
|
357
|
+
if (typeof result === 'function' && result.prototype instanceof Plugin) {
|
|
358
|
+
return new result()
|
|
359
|
+
}
|
|
360
|
+
return result
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// 支持对象形式: { name, version, install, start, ... }
|
|
364
|
+
class AnonymousPlugin extends Plugin {
|
|
365
|
+
constructor() {
|
|
366
|
+
super()
|
|
367
|
+
this.name = obj.name
|
|
368
|
+
this.version = obj.version || '1.0.0'
|
|
369
|
+
this.description = obj.description || ''
|
|
370
|
+
this.priority = obj.priority || 100
|
|
371
|
+
|
|
372
|
+
// 如果提供了 install/start/reload/uninstall 方法,绑定它们
|
|
373
|
+
if (typeof obj.install === 'function') {
|
|
374
|
+
this.install = obj.install.bind(this)
|
|
375
|
+
}
|
|
376
|
+
if (typeof obj.start === 'function') {
|
|
377
|
+
this.start = obj.start.bind(this)
|
|
378
|
+
}
|
|
379
|
+
if (typeof obj.reload === 'function') {
|
|
380
|
+
this.reload = obj.reload.bind(this)
|
|
381
|
+
}
|
|
382
|
+
if (typeof obj.uninstall === 'function') {
|
|
383
|
+
this.uninstall = obj.uninstall.bind(this)
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
return new AnonymousPlugin()
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
module.exports = { PluginManager }
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Provider 工厂
|
|
3
|
+
* 支持多种 AI 提供商
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { createOpenAICompatible } = require('@ai-sdk/openai-compatible')
|
|
7
|
+
const { createOpenAI } = require('@ai-sdk/openai')
|
|
8
|
+
const { createAnthropic } = require('@ai-sdk/anthropic')
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 默认提供商配置
|
|
12
|
+
*/
|
|
13
|
+
const DEFAULT_PROVIDERS = {
|
|
14
|
+
openai: {
|
|
15
|
+
name: 'OpenAI',
|
|
16
|
+
baseURL: 'https://api.openai.com/v1'
|
|
17
|
+
},
|
|
18
|
+
ollama: {
|
|
19
|
+
name: 'Ollama',
|
|
20
|
+
baseURL: 'http://localhost:11434/v1'
|
|
21
|
+
},
|
|
22
|
+
lmstudio: {
|
|
23
|
+
name: 'LM Studio',
|
|
24
|
+
baseURL: 'http://localhost:1234/v1'
|
|
25
|
+
},
|
|
26
|
+
deepseek: {
|
|
27
|
+
name: 'DeepSeek',
|
|
28
|
+
baseURL: 'https://api.deepseek.com/v1'
|
|
29
|
+
},
|
|
30
|
+
anthropic: {
|
|
31
|
+
name: 'Anthropic',
|
|
32
|
+
baseURL: 'https://api.anthropic.com/v1'
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 创建 AI 客户端
|
|
38
|
+
* @param {Object} config - 配置
|
|
39
|
+
* @param {string} config.provider - 提供商名称
|
|
40
|
+
* @param {string} config.model - 模型名称
|
|
41
|
+
* @param {string} config.apiKey - API 密钥
|
|
42
|
+
* @param {string} [config.baseURL] - 自定义 API 地址
|
|
43
|
+
*/
|
|
44
|
+
function createAI(config) {
|
|
45
|
+
const { provider, model, apiKey, baseURL } = config
|
|
46
|
+
const providerName = (provider || 'deepseek').toLowerCase()
|
|
47
|
+
|
|
48
|
+
// 检查是否是预定义提供商
|
|
49
|
+
if (DEFAULT_PROVIDERS[providerName]) {
|
|
50
|
+
const providerConfig = DEFAULT_PROVIDERS[providerName]
|
|
51
|
+
return createOpenAICompatible({
|
|
52
|
+
name: providerConfig.name,
|
|
53
|
+
baseURL: baseURL || providerConfig.baseURL,
|
|
54
|
+
apiKey: apiKey || 'dummy-key',
|
|
55
|
+
headers: provider === 'anthropic' ? {
|
|
56
|
+
'x-api-key': apiKey,
|
|
57
|
+
'anthropic-version': '2023-06-01'
|
|
58
|
+
} : undefined,
|
|
59
|
+
models: {
|
|
60
|
+
default: {
|
|
61
|
+
id: model || 'deepseek-chat',
|
|
62
|
+
streamOptions: {
|
|
63
|
+
includeUsage: true
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// 自定义提供商
|
|
71
|
+
return createOpenAICompatible({
|
|
72
|
+
name: providerName || 'Custom',
|
|
73
|
+
baseURL: baseURL,
|
|
74
|
+
apiKey: apiKey || 'dummy-key',
|
|
75
|
+
models: {
|
|
76
|
+
default: {
|
|
77
|
+
id: model || 'gpt-4'
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* 获取可用提供商列表
|
|
85
|
+
*/
|
|
86
|
+
function getAvailableProviders() {
|
|
87
|
+
return Object.entries(DEFAULT_PROVIDERS).map(([key, value]) => ({
|
|
88
|
+
id: key,
|
|
89
|
+
name: value.name,
|
|
90
|
+
baseURL: value.baseURL
|
|
91
|
+
}))
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 创建模型实例
|
|
96
|
+
* @param {string} model - 模型名称
|
|
97
|
+
* @param {Object} provider - Provider 实例
|
|
98
|
+
*/
|
|
99
|
+
function createModel(model, provider) {
|
|
100
|
+
return provider(model)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
module.exports = {
|
|
104
|
+
createAI,
|
|
105
|
+
createModel,
|
|
106
|
+
getAvailableProviders,
|
|
107
|
+
DEFAULT_PROVIDERS
|
|
108
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ToolRegistry 工具注册表
|
|
3
|
+
* 统一管理所有工具
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { EventEmitter } = require('../utils/event-emitter')
|
|
7
|
+
|
|
8
|
+
class ToolRegistry extends EventEmitter {
|
|
9
|
+
constructor() {
|
|
10
|
+
super()
|
|
11
|
+
this._tools = new Map()
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 注册工具
|
|
16
|
+
* @param {Object} tool - 工具定义
|
|
17
|
+
* @param {string} tool.name - 工具名称
|
|
18
|
+
* @param {string} tool.description - 工具描述
|
|
19
|
+
* @param {Function} tool.execute - 执行函数
|
|
20
|
+
* @param {Object} [tool.parameters] - 参数 schema
|
|
21
|
+
*/
|
|
22
|
+
register(tool) {
|
|
23
|
+
if (!tool.name) {
|
|
24
|
+
throw new Error('Tool must have a name')
|
|
25
|
+
}
|
|
26
|
+
if (typeof tool.execute !== 'function') {
|
|
27
|
+
throw new Error(`Tool '${tool.name}' must have an execute function`)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
this._tools.set(tool.name, {
|
|
31
|
+
name: tool.name,
|
|
32
|
+
description: tool.description || '',
|
|
33
|
+
inputSchema: tool.inputSchema || null,
|
|
34
|
+
parameters: tool.parameters || null,
|
|
35
|
+
execute: tool.execute
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
this.emit('tool:registered', tool)
|
|
39
|
+
return this
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 批量注册工具
|
|
44
|
+
* @param {Array<Object>} tools - 工具数组
|
|
45
|
+
*/
|
|
46
|
+
registerMany(tools) {
|
|
47
|
+
for (const tool of tools) {
|
|
48
|
+
this.register(tool)
|
|
49
|
+
}
|
|
50
|
+
return this
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 注销工具
|
|
55
|
+
* @param {string} name - 工具名称
|
|
56
|
+
*/
|
|
57
|
+
unregister(name) {
|
|
58
|
+
const tool = this._tools.get(name)
|
|
59
|
+
if (tool) {
|
|
60
|
+
this._tools.delete(name)
|
|
61
|
+
this.emit('tool:unregistered', tool)
|
|
62
|
+
}
|
|
63
|
+
return this
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 获取工具
|
|
68
|
+
* @param {string} name - 工具名称
|
|
69
|
+
* @returns {Object|undefined}
|
|
70
|
+
*/
|
|
71
|
+
get(name) {
|
|
72
|
+
return this._tools.get(name)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* 获取所有工具
|
|
77
|
+
* @returns {Array<Object>}
|
|
78
|
+
*/
|
|
79
|
+
getAll() {
|
|
80
|
+
return Array.from(this._tools.values())
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* 检查工具是否存在
|
|
85
|
+
* @param {string} name - 工具名称
|
|
86
|
+
* @returns {boolean}
|
|
87
|
+
*/
|
|
88
|
+
has(name) {
|
|
89
|
+
return this._tools.has(name)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* 执行工具
|
|
94
|
+
* @param {string} name - 工具名称
|
|
95
|
+
* @param {Object} args - 参数
|
|
96
|
+
* @param {Framework} framework - 框架实例
|
|
97
|
+
* @returns {Promise<any>}
|
|
98
|
+
*/
|
|
99
|
+
async execute(name, args, framework) {
|
|
100
|
+
const tool = this._tools.get(name)
|
|
101
|
+
if (!tool) {
|
|
102
|
+
throw new Error(`Tool '${name}' not found`)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
this.emit('tool:call', { name, args })
|
|
107
|
+
const result = await tool.execute(args, framework)
|
|
108
|
+
this.emit('tool:result', { name, args, result })
|
|
109
|
+
return result
|
|
110
|
+
} catch (err) {
|
|
111
|
+
this.emit('tool:error', { name, args, error: err })
|
|
112
|
+
throw err
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* 清空所有工具
|
|
118
|
+
*/
|
|
119
|
+
clear() {
|
|
120
|
+
this._tools.clear()
|
|
121
|
+
this.emit('tool:cleared')
|
|
122
|
+
return this
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* 获取工具数量
|
|
127
|
+
* @returns {number}
|
|
128
|
+
*/
|
|
129
|
+
size() {
|
|
130
|
+
return this._tools.size
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
module.exports = { ToolRegistry }
|