@nocobase/ai 2.1.0-beta.2 → 2.1.0-beta.20

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 (90) hide show
  1. package/LICENSE +201 -661
  2. package/README.md +79 -10
  3. package/lib/ai-employee-manager/index.d.ts +29 -0
  4. package/lib/ai-employee-manager/index.js +167 -0
  5. package/lib/ai-employee-manager/types.d.ts +56 -0
  6. package/lib/ai-employee-manager/types.js +24 -0
  7. package/lib/ai-manager.d.ts +8 -0
  8. package/lib/ai-manager.js +12 -0
  9. package/lib/document-loader/index.d.ts +10 -0
  10. package/lib/document-loader/index.js +90 -0
  11. package/lib/document-loader/loader.worker.d.ts +9 -0
  12. package/lib/document-loader/loader.worker.js +83 -0
  13. package/lib/document-loader/vendor/langchain/document_loaders/fs/text.d.ts +20 -0
  14. package/lib/document-loader/vendor/langchain/document_loaders/fs/text.js +99 -0
  15. package/lib/document-loader/xlsx.d.ts +10 -0
  16. package/lib/document-loader/xlsx.js +100 -0
  17. package/lib/index.d.ts +5 -0
  18. package/lib/index.js +11 -1
  19. package/lib/loader/employee.d.ts +37 -0
  20. package/lib/loader/employee.js +207 -0
  21. package/lib/loader/index.d.ts +3 -0
  22. package/lib/loader/index.js +7 -1
  23. package/lib/loader/mcp.d.ts +35 -0
  24. package/lib/loader/mcp.js +108 -0
  25. package/lib/loader/skills.d.ts +43 -0
  26. package/lib/loader/skills.js +138 -0
  27. package/lib/loader/tools.d.ts +1 -0
  28. package/lib/loader/tools.js +4 -3
  29. package/lib/mcp-manager/index.d.ts +43 -0
  30. package/lib/mcp-manager/index.js +341 -0
  31. package/lib/mcp-manager/types.d.ts +61 -0
  32. package/lib/mcp-manager/types.js +24 -0
  33. package/lib/mcp-tools-manager.d.ts +43 -0
  34. package/lib/mcp-tools-manager.js +77 -0
  35. package/lib/skills-manager/index.d.ts +29 -0
  36. package/lib/skills-manager/index.js +169 -0
  37. package/lib/skills-manager/types.d.ts +33 -0
  38. package/lib/skills-manager/types.js +24 -0
  39. package/lib/tools-manager/index.d.ts +2 -1
  40. package/lib/tools-manager/index.js +17 -7
  41. package/lib/tools-manager/types.d.ts +12 -4
  42. package/package.json +27 -7
  43. package/src/__tests__/ai-employees.test.ts +108 -0
  44. package/src/__tests__/mcp.test.ts +105 -0
  45. package/src/__tests__/resource/ai/ai-employees/index-employee/index.ts +16 -0
  46. package/src/__tests__/resource/ai/ai-employees/index-employee/prompt.md +1 -0
  47. package/src/__tests__/resource/ai/ai-employees/named-file-employee.ts +16 -0
  48. package/src/__tests__/resource/ai/ai-employees/with-skills/index.ts +16 -0
  49. package/src/__tests__/resource/ai/ai-employees/with-skills/skills/analysis/SKILLS.md +6 -0
  50. package/src/__tests__/resource/ai/ai-employees/with-skills-merge/index.ts +17 -0
  51. package/src/__tests__/resource/ai/ai-employees/with-skills-merge/skills/discovered-skill/SKILLS.md +6 -0
  52. package/src/__tests__/resource/ai/ai-employees/with-tools/index.ts +16 -0
  53. package/src/__tests__/resource/ai/ai-employees/with-tools/tools/discoveredTool.ts +23 -0
  54. package/src/__tests__/resource/ai/ai-employees/with-tools-merge/index.ts +16 -0
  55. package/src/__tests__/resource/ai/ai-employees/with-tools-merge/tools/discoveredTool.ts +23 -0
  56. package/src/__tests__/resource/ai/mcp/weather.ts +25 -0
  57. package/src/__tests__/resource/ai/skills/data-modeling/SKILLS.md +24 -0
  58. package/src/__tests__/resource/ai/skills/data-modeling/tools/read.ts +23 -0
  59. package/src/__tests__/resource/ai/skills/data-modeling/tools/search/description.md +1 -0
  60. package/src/__tests__/resource/ai/skills/data-modeling/tools/search/index.ts +23 -0
  61. package/src/__tests__/resource/ai/skills/document/tools/read.ts +1 -1
  62. package/src/__tests__/resource/ai/skills/document/tools/search/index.ts +1 -1
  63. package/src/__tests__/resource/ai/tools/desc/index.ts +1 -1
  64. package/src/__tests__/resource/ai/tools/group/group1.ts +1 -1
  65. package/src/__tests__/resource/ai/tools/group/group2.ts +1 -1
  66. package/src/__tests__/resource/ai/tools/group/group3/index.ts +1 -1
  67. package/src/__tests__/resource/ai/tools/hallow/index.ts +1 -1
  68. package/src/__tests__/resource/ai/tools/print.ts +1 -1
  69. package/src/__tests__/skills.test.ts +55 -0
  70. package/src/__tests__/tools.test.ts +5 -3
  71. package/src/ai-employee-manager/index.ts +148 -0
  72. package/src/ai-employee-manager/types.ts +63 -0
  73. package/src/ai-manager.ts +12 -0
  74. package/src/document-loader/index.ts +57 -0
  75. package/src/document-loader/loader.worker.ts +100 -0
  76. package/src/document-loader/vendor/langchain/document_loaders/fs/text.ts +72 -0
  77. package/src/document-loader/xlsx.ts +82 -0
  78. package/src/index.ts +5 -0
  79. package/src/loader/employee.ts +194 -0
  80. package/src/loader/index.ts +3 -0
  81. package/src/loader/mcp.ts +101 -0
  82. package/src/loader/skills.ts +129 -0
  83. package/src/loader/tools.ts +3 -2
  84. package/src/mcp-manager/index.ts +364 -0
  85. package/src/mcp-manager/types.ts +68 -0
  86. package/src/mcp-tools-manager.ts +90 -0
  87. package/src/skills-manager/index.ts +148 -0
  88. package/src/skills-manager/types.ts +38 -0
  89. package/src/tools-manager/index.ts +18 -7
  90. package/src/tools-manager/types.ts +13 -4
@@ -0,0 +1,105 @@
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 { createMockServer, MockServer } from '@nocobase/test';
11
+ import path from 'path';
12
+ import { AIManager } from '../ai-manager';
13
+ import { MCPLoader } from '../loader';
14
+ import { MCPManager } from '../mcp-manager';
15
+
16
+ describe('MCP loader test cases', () => {
17
+ const basePath = path.resolve(__dirname, 'resource', 'ai');
18
+ let app: MockServer;
19
+ let aiManager: AIManager;
20
+ let mcpManager: MCPManager;
21
+ let loader: MCPLoader;
22
+
23
+ beforeEach(async () => {
24
+ app = await createMockServer({
25
+ plugins: ['nocobase'],
26
+ });
27
+ await app.pm.enable('ai');
28
+ aiManager = app.aiManager;
29
+ mcpManager = aiManager.mcpManager;
30
+ loader = new MCPLoader(aiManager, {
31
+ scan: {
32
+ basePath,
33
+ pattern: ['**/mcp/*.ts', '!**/mcp/*.d.ts'],
34
+ },
35
+ });
36
+ });
37
+
38
+ afterEach(async () => {
39
+ await app.destroy();
40
+ });
41
+
42
+ it('should load mcp definitions in mcp root directory', async () => {
43
+ await loader.load();
44
+
45
+ const entry = await mcpManager.getMCP('weather');
46
+ expect(entry).toBeDefined();
47
+ expect(entry.name).toBe('weather');
48
+ expect(entry.enabled).toBe(true);
49
+ expect(entry.transport).toBe('http');
50
+ expect(entry.url).toBe('http://localhost:8123/mcp');
51
+ expect(entry.headers).toEqual({
52
+ Authorization: 'Bearer test-token',
53
+ });
54
+ expect(entry.env).toEqual({
55
+ MCP_ENV: 'test',
56
+ });
57
+ expect(entry.args).toEqual(['--foo']);
58
+ expect(entry.restart).toEqual({
59
+ enabled: true,
60
+ });
61
+
62
+ const enabledEntries = await mcpManager.listMCP({ enabled: true, transport: 'http', name: 'weath' });
63
+ expect(enabledEntries.map((item) => item.name)).toEqual(['weather']);
64
+ });
65
+
66
+ it('should expose cached mcp tools and allow updating permissions', async () => {
67
+ const manager = mcpManager as any;
68
+ manager.toolsMap = {
69
+ weather: [
70
+ {
71
+ name: 'getForecast',
72
+ description: 'Get weather forecast',
73
+ },
74
+ {
75
+ name: 'setDefaultCity',
76
+ description: 'Set default city',
77
+ },
78
+ ],
79
+ };
80
+
81
+ const tools = await mcpManager.listMCPTools();
82
+ expect(tools.weather).toEqual([
83
+ {
84
+ name: 'mcp-weather-getForecast',
85
+ title: 'getForecast',
86
+ description: 'Get weather forecast',
87
+ serverName: 'weather',
88
+ permission: 'ASK',
89
+ },
90
+ {
91
+ name: 'mcp-weather-setDefaultCity',
92
+ title: 'setDefaultCity',
93
+ description: 'Set default city',
94
+ serverName: 'weather',
95
+ permission: 'ASK',
96
+ },
97
+ ]);
98
+
99
+ await mcpManager.updateMCPToolPermission('mcp-weather-getForecast', 'ALLOW');
100
+
101
+ const updatedTools = await mcpManager.listMCPTools();
102
+ expect(updatedTools.weather[0].permission).toBe('ALLOW');
103
+ expect(updatedTools.weather[1].permission).toBe('ASK');
104
+ });
105
+ });
@@ -0,0 +1,16 @@
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 { defineAIEmployee } from '@nocobase/ai';
11
+
12
+ export default defineAIEmployee({
13
+ username: 'index-employee',
14
+ nickname: 'Index Employee',
15
+ tools: [],
16
+ });
@@ -0,0 +1 @@
1
+ Prompt from markdown file.
@@ -0,0 +1,16 @@
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 { defineAIEmployee } from '@nocobase/ai';
11
+
12
+ export default defineAIEmployee({
13
+ username: 'named-file-employee',
14
+ nickname: 'Named File Employee',
15
+ tools: [],
16
+ });
@@ -0,0 +1,16 @@
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 { defineAIEmployee } from '@nocobase/ai';
11
+
12
+ export default defineAIEmployee({
13
+ username: 'with-skills',
14
+ nickname: 'With Skills',
15
+ tools: [],
16
+ });
@@ -0,0 +1,6 @@
1
+ ---
2
+ name: analysis
3
+ description: analysis skill
4
+ ---
5
+
6
+ Analysis skill content.
@@ -0,0 +1,17 @@
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 { defineAIEmployee } from '@nocobase/ai';
11
+
12
+ export default defineAIEmployee({
13
+ username: 'with-skills-merge',
14
+ nickname: 'With Skills Merge',
15
+ tools: [],
16
+ skills: ['configured-skill'],
17
+ });
@@ -0,0 +1,6 @@
1
+ ---
2
+ name: discovered-skill
3
+ description: discovered skill
4
+ ---
5
+
6
+ Discovered skill content.
@@ -0,0 +1,16 @@
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 { defineAIEmployee } from '@nocobase/ai';
11
+
12
+ export default defineAIEmployee({
13
+ username: 'with-tools',
14
+ nickname: 'With Tools',
15
+ tools: [],
16
+ });
@@ -0,0 +1,23 @@
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 { Context } from '@nocobase/actions';
11
+ import { defineTools } from '@nocobase/ai';
12
+
13
+ export default defineTools({
14
+ scope: 'SPECIFIED',
15
+ definition: {
16
+ name: 'discoveredTool',
17
+ description: 'discovered tool',
18
+ schema: null,
19
+ },
20
+ invoke: async (ctx: Context, args: any) => {
21
+ return { status: 'success' };
22
+ },
23
+ });
@@ -0,0 +1,16 @@
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 { defineAIEmployee } from '@nocobase/ai';
11
+
12
+ export default defineAIEmployee({
13
+ username: 'with-tools-merge',
14
+ nickname: 'With Tools Merge',
15
+ tools: [{ name: 'configuredTool', autoCall: true }],
16
+ });
@@ -0,0 +1,23 @@
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 { Context } from '@nocobase/actions';
11
+ import { defineTools } from '@nocobase/ai';
12
+
13
+ export default defineTools({
14
+ scope: 'SPECIFIED',
15
+ definition: {
16
+ name: 'discoveredTool',
17
+ description: 'discovered tool in merge employee',
18
+ schema: null,
19
+ },
20
+ invoke: async (ctx: Context, args: any) => {
21
+ return { status: 'success' };
22
+ },
23
+ });
@@ -0,0 +1,25 @@
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 { defineMCP } from '../../../../index';
11
+
12
+ export default defineMCP({
13
+ transport: 'http',
14
+ url: 'http://localhost:8123/mcp',
15
+ headers: {
16
+ Authorization: 'Bearer test-token',
17
+ },
18
+ env: {
19
+ MCP_ENV: 'test',
20
+ },
21
+ args: ['--foo'],
22
+ restart: {
23
+ enabled: true,
24
+ },
25
+ });
@@ -0,0 +1,24 @@
1
+ ---
2
+ name: data-modeling
3
+ description: helps translate business scenarios into normalized database schemas with table declarations and relationship diagrams.
4
+ ---
5
+
6
+ You help users design or improve database schemas using structured collection definitions.
7
+
8
+ When user decisions are required, always invoke the \`dataModeling-suggestions\` tool to provide selectable options, enabling users to reply quickly and continue the conversation.
9
+
10
+ # Global Constraints
11
+ - **Language**: Always respond in the same language as the user's input (e.g., if the user asks in Chinese, reply in Chinese).
12
+ - **Tone**: Professional, precise, and helpful.
13
+ - **Interactivity**: Before proceeding to the next step, always ask for the user’s suggestions before taking any action.
14
+ - **Tool usage**:
15
+ 1. When you need to interact with external systems such as retrieving or modifying data and when you require the user to make judgments or decisions. You MUST respond with a tool call.
16
+ 2. When using a tool, if the execution result is a failure or an exception occurs during execution, explain the issue to the user in clear and concise language, combining the tool’s feedback with your own description of the problem.
17
+ - **Data standards**: When defining options or enums, the value part must consist only of letters, underscores, and numbers.
18
+
19
+ # Available Tools
20
+ - \`dataModeling-getCollectionNames\`: Lists all tables with their internal name and display title. Use this to disambiguate user references.
21
+ - \`dataModeling-getCollectionMetadata\`: Returns detailed field definitions and relationships for specified tables.
22
+ - \`dataModeling-defineCollections\`: Submits new or updated schema definitions to the system. Do not assume success until a tool response is received.
23
+ - \`dataModeling-intentRouter\`: Route intents to appropriate workflow
24
+ - \`dataModeling-suggestions\`: Provide a list of suggested prompts for the user to choose from.
@@ -0,0 +1,23 @@
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 { Context } from '@nocobase/actions';
11
+ import { defineTools } from '@nocobase/ai';
12
+
13
+ export default defineTools({
14
+ scope: 'SPECIFIED',
15
+ definition: {
16
+ name: 'read',
17
+ description: 'read document',
18
+ schema: null,
19
+ },
20
+ invoke: async (ctx: Context, args: any) => {
21
+ return { status: 'success' };
22
+ },
23
+ });
@@ -0,0 +1,23 @@
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 { Context } from '@nocobase/actions';
11
+ import { defineTools } from '@nocobase/ai';
12
+
13
+ export default defineTools({
14
+ scope: 'SPECIFIED',
15
+ definition: {
16
+ name: 'search',
17
+ description: 'search document',
18
+ schema: null,
19
+ },
20
+ invoke: async (ctx: Context, args: any) => {
21
+ return { status: 'success' };
22
+ },
23
+ });
@@ -17,7 +17,7 @@ export default defineTools({
17
17
  description: 'read document',
18
18
  schema: null,
19
19
  },
20
- invoke: async (ctx: Context, args: any, id: string) => {
20
+ invoke: async (ctx: Context, args: any) => {
21
21
  return { status: 'success' };
22
22
  },
23
23
  });
@@ -17,7 +17,7 @@ export default defineTools({
17
17
  description: 'search document',
18
18
  schema: null,
19
19
  },
20
- invoke: async (ctx: Context, args: any, id: string) => {
20
+ invoke: async (ctx: Context, args: any) => {
21
21
  return { status: 'success' };
22
22
  },
23
23
  });
@@ -17,7 +17,7 @@ export default defineTools({
17
17
  description: 'tools with description.md',
18
18
  schema: null,
19
19
  },
20
- invoke: async (ctx: Context, args: any, id: string) => {
20
+ invoke: async (ctx: Context, args: any) => {
21
21
  return { status: 'success' };
22
22
  },
23
23
  });
@@ -17,7 +17,7 @@ export default defineTools({
17
17
  description: 'hallow group1',
18
18
  schema: null,
19
19
  },
20
- invoke: async (ctx: Context, args: any, id: string) => {
20
+ invoke: async (ctx: Context, args: any) => {
21
21
  return { status: 'success' };
22
22
  },
23
23
  });
@@ -17,7 +17,7 @@ export default defineTools({
17
17
  description: 'hallow group2',
18
18
  schema: null,
19
19
  },
20
- invoke: async (ctx: Context, args: any, id: string) => {
20
+ invoke: async (ctx: Context, args: any) => {
21
21
  return { status: 'success' };
22
22
  },
23
23
  });
@@ -17,7 +17,7 @@ export default defineTools({
17
17
  description: 'hallow group3',
18
18
  schema: null,
19
19
  },
20
- invoke: async (ctx: Context, args: any, id: string) => {
20
+ invoke: async (ctx: Context, args: any) => {
21
21
  return { status: 'success' };
22
22
  },
23
23
  });
@@ -17,7 +17,7 @@ export default defineTools({
17
17
  description: 'hallow tools',
18
18
  schema: null,
19
19
  },
20
- invoke: async (ctx: Context, args: any, id: string) => {
20
+ invoke: async (ctx: Context, args: any) => {
21
21
  return { status: 'success' };
22
22
  },
23
23
  });
@@ -17,7 +17,7 @@ export default defineTools({
17
17
  description: 'print tools',
18
18
  schema: null,
19
19
  },
20
- invoke: async (ctx: Context, args: any, id: string) => {
20
+ invoke: async (ctx: Context, args: any) => {
21
21
  return { status: 'success' };
22
22
  },
23
23
  });
@@ -0,0 +1,55 @@
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 { createMockServer, MockServer } from '@nocobase/test';
11
+ import { SkillsLoader } from '../loader';
12
+ import path from 'path';
13
+ import { AIManager } from '../ai-manager';
14
+ import { SkillsManager } from '../skills-manager';
15
+ import { readFile } from 'fs/promises';
16
+ import matter from 'gray-matter';
17
+
18
+ describe('Skills loader test cases', () => {
19
+ const basePath = path.resolve(__dirname, 'resource', 'ai');
20
+ let app: MockServer;
21
+ let aiManager: AIManager;
22
+ let skillsManager: SkillsManager;
23
+ let loader: SkillsLoader;
24
+
25
+ beforeEach(async () => {
26
+ app = await createMockServer({
27
+ plugins: ['nocobase'],
28
+ });
29
+ // with mysql add ai plugin in createMockServer will occur error with create usersAiEmployees collection at app startup
30
+ await app.pm.enable('ai');
31
+ aiManager = app.aiManager;
32
+ skillsManager = aiManager.skillsManager;
33
+ loader = new SkillsLoader(aiManager, {
34
+ scan: { basePath, pattern: ['**/skills/**/SKILLS.md'] },
35
+ });
36
+ });
37
+
38
+ afterEach(async () => {
39
+ await app.destroy();
40
+ });
41
+
42
+ it('should load skills in skills root directory ', async () => {
43
+ await loader.load();
44
+ const skillsMarkdown = await readFile(path.resolve(basePath, 'skills', 'data-modeling', 'SKILLS.md'), 'utf-8');
45
+ const { content } = matter(skillsMarkdown);
46
+ const skills = await skillsManager.getSkills('data-modeling');
47
+ expect(skills).toBeDefined();
48
+ expect(skills.name).eq('data-modeling');
49
+ expect(skills.description).eq(
50
+ 'helps translate business scenarios into normalized database schemas with table declarations and relationship diagrams.',
51
+ );
52
+ expect(skills.content).eq(content);
53
+ expect(skills.tools.sort()).toEqual(['search', 'read'].sort());
54
+ });
55
+ });
@@ -13,6 +13,8 @@ import path from 'path';
13
13
  import { AIManager } from '../ai-manager';
14
14
  import { ToolsManager } from '../tools-manager';
15
15
 
16
+ const normalizeEOL = (value: string) => value.replace(/\r\n?/g, '\n');
17
+
16
18
  describe('Tools loader test cases', () => {
17
19
  const basePath = path.join(process.cwd(), 'packages/core/ai/src/__tests__/resource/ai');
18
20
  let app: MockServer;
@@ -59,7 +61,7 @@ describe('Tools loader test cases', () => {
59
61
  const tools = await toolsManager.getTools('desc');
60
62
  expect(tools).toBeDefined();
61
63
  expect(tools.definition.name).eq('desc');
62
- expect(tools.definition.description).eq('# DESC\n');
64
+ expect(normalizeEOL(tools.definition.description)).eq('# DESC\n');
63
65
  expect(await tools.invoke(null, null, null)).toEqual({ status: 'success' });
64
66
  expect(await tools.invoke(null, null, null)).toEqual({ status: 'success' });
65
67
  });
@@ -78,7 +80,7 @@ describe('Tools loader test cases', () => {
78
80
  const tools = await toolsManager.getTools('search');
79
81
  expect(tools).toBeDefined();
80
82
  expect(tools.definition.name).eq('search');
81
- expect(tools.definition.description).eq('# SEARCH\n');
83
+ expect(normalizeEOL(tools.definition.description)).eq('# SEARCH\n');
82
84
  expect(await tools.invoke(null, null, null)).toEqual({ status: 'success' });
83
85
  });
84
86
 
@@ -99,7 +101,7 @@ describe('Tools loader test cases', () => {
99
101
  const group3 = await toolsManager.getTools('group3');
100
102
  expect(group3).toBeDefined();
101
103
  expect(group3.definition.name).eq('group3');
102
- expect(group3.definition.description).eq('# GROUP3\n');
104
+ expect(normalizeEOL(group3.definition.description)).eq('# GROUP3\n');
103
105
  expect(await group3.invoke(null, null, null)).toEqual({ status: 'success' });
104
106
  });
105
107