@nocobase/ai 2.1.0-beta.14 → 2.1.0-beta.16

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.
Files changed (87) hide show
  1. package/lib/ai-employee-manager/index.d.ts +29 -0
  2. package/lib/ai-employee-manager/index.js +167 -0
  3. package/lib/ai-employee-manager/types.d.ts +56 -0
  4. package/lib/ai-employee-manager/types.js +24 -0
  5. package/lib/ai-manager.d.ts +8 -0
  6. package/lib/ai-manager.js +12 -0
  7. package/lib/document-loader/index.d.ts +10 -0
  8. package/lib/document-loader/index.js +90 -0
  9. package/lib/document-loader/loader.worker.d.ts +9 -0
  10. package/lib/document-loader/loader.worker.js +83 -0
  11. package/lib/document-loader/vendor/langchain/document_loaders/fs/text.d.ts +20 -0
  12. package/lib/document-loader/vendor/langchain/document_loaders/fs/text.js +99 -0
  13. package/lib/document-loader/xlsx.d.ts +10 -0
  14. package/lib/document-loader/xlsx.js +100 -0
  15. package/lib/index.d.ts +5 -0
  16. package/lib/index.js +11 -1
  17. package/lib/loader/employee.d.ts +37 -0
  18. package/lib/loader/employee.js +207 -0
  19. package/lib/loader/index.d.ts +3 -0
  20. package/lib/loader/index.js +7 -1
  21. package/lib/loader/mcp.d.ts +35 -0
  22. package/lib/loader/mcp.js +108 -0
  23. package/lib/loader/skills.d.ts +43 -0
  24. package/lib/loader/skills.js +138 -0
  25. package/lib/loader/tools.d.ts +1 -0
  26. package/lib/loader/tools.js +4 -3
  27. package/lib/mcp-manager/index.d.ts +43 -0
  28. package/lib/mcp-manager/index.js +341 -0
  29. package/lib/mcp-manager/types.d.ts +61 -0
  30. package/lib/mcp-manager/types.js +24 -0
  31. package/lib/mcp-tools-manager.d.ts +43 -0
  32. package/lib/mcp-tools-manager.js +77 -0
  33. package/lib/skills-manager/index.d.ts +29 -0
  34. package/lib/skills-manager/index.js +169 -0
  35. package/lib/skills-manager/types.d.ts +33 -0
  36. package/lib/skills-manager/types.js +24 -0
  37. package/lib/tools-manager/index.d.ts +2 -1
  38. package/lib/tools-manager/index.js +17 -7
  39. package/lib/tools-manager/types.d.ts +12 -4
  40. package/package.json +11 -6
  41. package/src/__tests__/ai-employees.test.ts +108 -0
  42. package/src/__tests__/mcp.test.ts +105 -0
  43. package/src/__tests__/resource/ai/ai-employees/index-employee/index.ts +16 -0
  44. package/src/__tests__/resource/ai/ai-employees/index-employee/prompt.md +1 -0
  45. package/src/__tests__/resource/ai/ai-employees/named-file-employee.ts +16 -0
  46. package/src/__tests__/resource/ai/ai-employees/with-skills/index.ts +16 -0
  47. package/src/__tests__/resource/ai/ai-employees/with-skills/skills/analysis/SKILLS.md +6 -0
  48. package/src/__tests__/resource/ai/ai-employees/with-skills-merge/index.ts +17 -0
  49. package/src/__tests__/resource/ai/ai-employees/with-skills-merge/skills/discovered-skill/SKILLS.md +6 -0
  50. package/src/__tests__/resource/ai/ai-employees/with-tools/index.ts +16 -0
  51. package/src/__tests__/resource/ai/ai-employees/with-tools/tools/discoveredTool.ts +23 -0
  52. package/src/__tests__/resource/ai/ai-employees/with-tools-merge/index.ts +16 -0
  53. package/src/__tests__/resource/ai/ai-employees/with-tools-merge/tools/discoveredTool.ts +23 -0
  54. package/src/__tests__/resource/ai/mcp/weather.ts +25 -0
  55. package/src/__tests__/resource/ai/skills/data-modeling/SKILLS.md +24 -0
  56. package/src/__tests__/resource/ai/skills/data-modeling/tools/read.ts +23 -0
  57. package/src/__tests__/resource/ai/skills/data-modeling/tools/search/description.md +1 -0
  58. package/src/__tests__/resource/ai/skills/data-modeling/tools/search/index.ts +23 -0
  59. package/src/__tests__/resource/ai/skills/document/tools/read.ts +1 -1
  60. package/src/__tests__/resource/ai/skills/document/tools/search/index.ts +1 -1
  61. package/src/__tests__/resource/ai/tools/desc/index.ts +1 -1
  62. package/src/__tests__/resource/ai/tools/group/group1.ts +1 -1
  63. package/src/__tests__/resource/ai/tools/group/group2.ts +1 -1
  64. package/src/__tests__/resource/ai/tools/group/group3/index.ts +1 -1
  65. package/src/__tests__/resource/ai/tools/hallow/index.ts +1 -1
  66. package/src/__tests__/resource/ai/tools/print.ts +1 -1
  67. package/src/__tests__/skills.test.ts +55 -0
  68. package/src/ai-employee-manager/index.ts +148 -0
  69. package/src/ai-employee-manager/types.ts +63 -0
  70. package/src/ai-manager.ts +12 -0
  71. package/src/document-loader/index.ts +57 -0
  72. package/src/document-loader/loader.worker.ts +100 -0
  73. package/src/document-loader/vendor/langchain/document_loaders/fs/text.ts +72 -0
  74. package/src/document-loader/xlsx.ts +82 -0
  75. package/src/index.ts +5 -0
  76. package/src/loader/employee.ts +194 -0
  77. package/src/loader/index.ts +3 -0
  78. package/src/loader/mcp.ts +101 -0
  79. package/src/loader/skills.ts +129 -0
  80. package/src/loader/tools.ts +3 -2
  81. package/src/mcp-manager/index.ts +364 -0
  82. package/src/mcp-manager/types.ts +68 -0
  83. package/src/mcp-tools-manager.ts +90 -0
  84. package/src/skills-manager/index.ts +148 -0
  85. package/src/skills-manager/types.ts +38 -0
  86. package/src/tools-manager/index.ts +18 -7
  87. package/src/tools-manager/types.ts +13 -4
@@ -0,0 +1,90 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ import { Registry } from '@nocobase/utils';
11
+
12
+ export type McpTool = {
13
+ name: string;
14
+ description: string;
15
+ inputSchema?: any;
16
+ resourceName?: string;
17
+ actionName?: string;
18
+ path?: string;
19
+ method?: string;
20
+ call: (args: Record<string, any>, context?: McpToolCallContext) => Promise<any>;
21
+ };
22
+
23
+ export type McpToolCallContext = {
24
+ token?: string;
25
+ headers?: Record<string, string | string[] | undefined>;
26
+ };
27
+
28
+ export type McpToolResultPostProcessorContext = {
29
+ tool: McpTool;
30
+ args: Record<string, any>;
31
+ callContext?: McpToolCallContext;
32
+ response?: {
33
+ statusCode?: number;
34
+ headers?: Record<string, any>;
35
+ body?: any;
36
+ };
37
+ };
38
+
39
+ export type McpToolResultPostProcessor = (
40
+ result: any,
41
+ context: McpToolResultPostProcessorContext,
42
+ ) => any | Promise<any>;
43
+
44
+ export class McpToolsManager {
45
+ private tools = new Registry<McpTool>();
46
+ private resultPostProcessors = new Map<string, McpToolResultPostProcessor[]>();
47
+
48
+ private getActionKey(resourceName: string, actionName: string) {
49
+ return `${resourceName}:${actionName}`;
50
+ }
51
+
52
+ registerTools(tools: McpTool[]) {
53
+ for (const tool of tools) {
54
+ this.tools.register(tool.name, tool);
55
+ }
56
+ }
57
+
58
+ registerToolResultPostProcessor(resourceName: string, actionName: string, processor: McpToolResultPostProcessor) {
59
+ const key = this.getActionKey(resourceName, actionName);
60
+ const processors = this.resultPostProcessors.get(key) || [];
61
+ processors.push(processor);
62
+ this.resultPostProcessors.set(key, processors);
63
+ }
64
+
65
+ async postProcessToolResult(tool: McpTool, result: any, context: Omit<McpToolResultPostProcessorContext, 'tool'>) {
66
+ if (!tool.resourceName || !tool.actionName) {
67
+ return result;
68
+ }
69
+
70
+ const processors = this.resultPostProcessors.get(this.getActionKey(tool.resourceName, tool.actionName)) || [];
71
+ let current = result;
72
+
73
+ for (const processor of processors) {
74
+ current = await processor(current, {
75
+ ...context,
76
+ tool,
77
+ });
78
+ }
79
+
80
+ return current;
81
+ }
82
+
83
+ listTools() {
84
+ return [...this.tools.getValues()];
85
+ }
86
+
87
+ getTool(name: string) {
88
+ return this.tools.get(name);
89
+ }
90
+ }
@@ -0,0 +1,148 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ import { Op } from '@nocobase/database';
11
+ import { SkillsEntry, SkillsManager, SkillsOptions, SkillsFilter } from './types';
12
+ import { SequelizeCollectionManager } from '@nocobase/data-source-manager';
13
+ import { Registry } from '@nocobase/utils';
14
+ import _ from 'lodash';
15
+
16
+ export class DefaultSkillsManager implements SkillsManager {
17
+ private readonly skills = new Registry<SkillsEntry>();
18
+ private readonly provideCollectionManager: () => { collectionManager: SequelizeCollectionManager };
19
+ private mode = 'memory';
20
+
21
+ constructor(private readonly app: any) {
22
+ this.provideCollectionManager = () => app.mainDataSource;
23
+ }
24
+
25
+ async init() {
26
+ if (this.mode === 'memory') {
27
+ await this.persistence();
28
+ this.mode = 'database';
29
+ }
30
+ }
31
+
32
+ getSkills(name: string[]): Promise<SkillsEntry[]>;
33
+ getSkills(name: string): Promise<SkillsEntry>;
34
+ async getSkills(name: string | string[]): Promise<SkillsEntry | SkillsEntry[]> {
35
+ if (_.isArray(name)) {
36
+ return (await this.aiSkillsModel.findAll({ where: { name: { [Op.in]: name } } }))
37
+ .map((it) => it.toJSON())
38
+ .map(converterSkillsEntry);
39
+ } else {
40
+ return converterSkillsEntry((await this.aiSkillsModel.findOne({ where: { name } }))?.toJSON()) as SkillsEntry;
41
+ }
42
+ }
43
+
44
+ async listSkills(filter: SkillsFilter): Promise<SkillsEntry[]> {
45
+ const where = {};
46
+ if (filter?.scope) {
47
+ where['scope'] = filter.scope;
48
+ }
49
+ if (filter?.name) {
50
+ where['name'] = {
51
+ [Op.substring]: filter.name,
52
+ };
53
+ }
54
+ return (await this.aiSkillsModel.findAll({ where })).map((it) => it.toJSON()).map(converterSkillsEntry);
55
+ }
56
+
57
+ async registerSkills(options: SkillsOptions): Promise<void> {
58
+ if (this.mode === 'memory') {
59
+ return this.registerSkillsInMemory(options);
60
+ }
61
+ return this.registerSkillsInDatabase(options);
62
+ }
63
+
64
+ async persistence(): Promise<void> {
65
+ const skillsList = [...this.skills.getValues()];
66
+ for (const skill of skillsList) {
67
+ await this.registerSkillsInDatabase(skill);
68
+ }
69
+ }
70
+
71
+ private registerSkillsInMemory(options: SkillsOptions): void {
72
+ const skillsEntry: SkillsEntry = { ...options };
73
+ this.skills.register(options.name, skillsEntry);
74
+ }
75
+
76
+ async registerSkillsInDatabase(options: SkillsOptions): Promise<void> {
77
+ const title = options.introduction?.title;
78
+ const about = options.introduction?.about;
79
+ const from = options.from || 'loader';
80
+
81
+ await this.sequelize.transaction(async (transaction) => {
82
+ const existed = await this.aiSkillsModel.findOne({ where: { name: options.name }, transaction });
83
+ if (existed) {
84
+ await this.aiSkillsModel.update(
85
+ {
86
+ scope: options.scope,
87
+ description: options.description,
88
+ content: options.content,
89
+ tools: options.tools,
90
+ title,
91
+ about,
92
+ from,
93
+ },
94
+ {
95
+ where: { name: options.name },
96
+ transaction,
97
+ },
98
+ );
99
+ } else {
100
+ await this.aiSkillsModel.create(
101
+ {
102
+ name: options.name,
103
+ scope: options.scope,
104
+ description: options.description,
105
+ content: options.content,
106
+ tools: options.tools,
107
+ title,
108
+ about,
109
+ from,
110
+ },
111
+ {
112
+ transaction,
113
+ },
114
+ );
115
+ }
116
+ });
117
+ }
118
+
119
+ private get aiSkillsCollection() {
120
+ return this.collectionManager.getCollection('aiSkills');
121
+ }
122
+
123
+ private get aiSkillsModel() {
124
+ return this.aiSkillsCollection?.model;
125
+ }
126
+
127
+ private get sequelize() {
128
+ return this.collectionManager.db.sequelize;
129
+ }
130
+
131
+ private get collectionManager() {
132
+ return this.provideCollectionManager().collectionManager;
133
+ }
134
+ }
135
+
136
+ const converterSkillsEntry = (model: any): SkillsEntry => {
137
+ return {
138
+ ...(model ?? {}),
139
+ introduction: model?.title
140
+ ? {
141
+ title: model.title,
142
+ about: model?.about,
143
+ }
144
+ : undefined,
145
+ };
146
+ };
147
+
148
+ export * from './types';
@@ -0,0 +1,38 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ export interface SkillsManager {
11
+ init(): Promise<void>;
12
+ getSkills(name: string[]): Promise<SkillsEntry[]>;
13
+ getSkills(name: string): Promise<SkillsEntry>;
14
+ listSkills(filter?: SkillsFilter): Promise<SkillsEntry[]>;
15
+ registerSkills(options: SkillsOptions): Promise<void>;
16
+ }
17
+
18
+ export type SkillsOptions = {
19
+ scope: SkillsScope;
20
+ name: string;
21
+ description: string;
22
+ content: string;
23
+ tools?: string[];
24
+ introduction?: {
25
+ title: string;
26
+ about?: string;
27
+ };
28
+ from?: string;
29
+ };
30
+
31
+ export type SkillsEntry = SkillsOptions;
32
+
33
+ export type SkillsFilter = {
34
+ scope?: SkillsScope;
35
+ name?: string;
36
+ };
37
+
38
+ export type SkillsScope = 'SPECIFIED' | 'GENERAL' | 'CUSTOM';
@@ -17,17 +17,17 @@ export class DefaultToolsManager implements ToolsManager {
17
17
  private readonly dynamicTools: DynamicToolsProvider[] = [],
18
18
  ) {}
19
19
 
20
- async getTools(toolName: string): Promise<ToolsEntry> {
20
+ async getTools(toolName: string, filter?: ToolsFilter): Promise<ToolsEntry> {
21
21
  const target = this.tools.get(toolName);
22
22
  if (target) {
23
23
  return target;
24
24
  }
25
- const dynamicTools = await this.syncDynamicTools();
25
+ const dynamicTools = await this.syncDynamicTools(filter);
26
26
  return dynamicTools.find((x) => x.definition.name === toolName);
27
27
  }
28
28
 
29
29
  async listTools(filter?: ToolsFilter): Promise<ToolsEntry[]> {
30
- const toolsList = await this.getToolsList();
30
+ const toolsList = await this.getToolsList(filter);
31
31
  return toolsList.filter((x) => {
32
32
  if (!filter) {
33
33
  return true;
@@ -50,10 +50,21 @@ export class DefaultToolsManager implements ToolsManager {
50
50
  });
51
51
  }
52
52
 
53
+ isToolsExisted(toolName: string): boolean {
54
+ const target = this.tools.get(toolName);
55
+ if (target) {
56
+ return true;
57
+ }
58
+ return false;
59
+ }
60
+
53
61
  registerTools(options: ToolsOptions | ToolsOptions[]): void {
54
62
  const list = _.isArray(options) ? options : [options];
55
63
  for (const item of list) {
56
64
  const toolsEntry = { ...item } as ToolsEntry;
65
+ if (!toolsEntry.from) {
66
+ toolsEntry.from = 'loader';
67
+ }
57
68
  if (!toolsEntry.execution) {
58
69
  toolsEntry.execution = 'backend';
59
70
  }
@@ -74,18 +85,18 @@ export class DefaultToolsManager implements ToolsManager {
74
85
  this.dynamicTools.push(provider);
75
86
  }
76
87
 
77
- private async getToolsList(): Promise<ToolsEntry[]> {
78
- const dynamicTools = await this.syncDynamicTools();
88
+ private async getToolsList(filter?: ToolsFilter): Promise<ToolsEntry[]> {
89
+ const dynamicTools = await this.syncDynamicTools(filter);
79
90
  return [...this.tools.getValues(), ...dynamicTools];
80
91
  }
81
92
 
82
- private async syncDynamicTools(): Promise<ToolsEntry[]> {
93
+ private async syncDynamicTools(filter?: ToolsFilter): Promise<ToolsEntry[]> {
83
94
  if (this.dynamicTools.length === 0) {
84
95
  return [];
85
96
  }
86
97
  const registry = new Registry<ToolsEntry>();
87
98
  const ephemeral = new DefaultToolsManager(registry);
88
- await Promise.all(this.dynamicTools.map((register) => register(ephemeral)));
99
+ await Promise.all(this.dynamicTools.map((register) => register(ephemeral, filter)));
89
100
  return [...registry.getValues()];
90
101
  }
91
102
  }
@@ -10,17 +10,19 @@
10
10
  import type { Context } from '@nocobase/actions';
11
11
 
12
12
  export interface ToolsManager extends ToolsRegistration {
13
- getTools(toolName: string): Promise<ToolsEntry>;
13
+ getTools(toolName: string, filter?: ToolsFilter): Promise<ToolsEntry>;
14
14
  listTools(filter?: ToolsFilter): Promise<ToolsEntry[]>;
15
+ isToolsExisted(toolName: string): boolean;
15
16
  }
16
17
 
17
18
  export interface ToolsRegistration {
18
19
  registerTools(options: ToolsOptions | ToolsOptions[]): void;
19
- registerDynamicTools(provider: (register: ToolsRegistration) => Promise<void>): void;
20
+ registerDynamicTools(provider: DynamicToolsProvider): void;
20
21
  }
21
22
 
22
23
  export type ToolsOptions = {
23
24
  scope: Scope;
25
+ from?: From;
24
26
  execution?: 'frontend' | 'backend';
25
27
  defaultPermission?: Permission;
26
28
  silence?: boolean;
@@ -33,18 +35,25 @@ export type ToolsOptions = {
33
35
  description: string;
34
36
  schema?: any;
35
37
  };
36
- invoke: (ctx: Context, args: any, id: string) => Promise<any>;
38
+ invoke: (ctx: Context, args: any, runtime: ToolsRuntime) => Promise<any>;
39
+ };
40
+
41
+ export type ToolsRuntime = {
42
+ toolCallId: string;
43
+ writer: (chunk: any) => void;
37
44
  };
38
45
 
39
46
  export type ToolsEntry = ToolsOptions;
40
47
 
41
48
  export type Scope = 'SPECIFIED' | 'GENERAL' | 'CUSTOM';
42
49
  export type Permission = 'ASK' | 'ALLOW';
50
+ export type From = 'loader' | 'workflow' | 'mcp';
43
51
 
44
- export type DynamicToolsProvider = (register: ToolsRegistration) => Promise<void>;
52
+ export type DynamicToolsProvider = (register: ToolsRegistration, filter?: ToolsFilter) => Promise<void>;
45
53
 
46
54
  export type ToolsFilter = {
47
55
  scope?: Scope;
48
56
  defaultPermission?: Permission;
49
57
  silence?: boolean;
58
+ sessionId?: string;
50
59
  };