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

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,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 { SequelizeCollectionManager } from '@nocobase/data-source-manager';
12
+ import { Registry } from '@nocobase/utils';
13
+ import _ from 'lodash';
14
+ import { AIEmployeeEntry, AIEmployeeFilter, AIEmployeeManager, AIEmployeeOptions } from './types';
15
+
16
+ const DEFAULT_KNOWLEDGE_BASE = {
17
+ topK: 3,
18
+ score: '0.6',
19
+ knowledgeBaseIds: [],
20
+ };
21
+ const DEFAULT_KNOWLEDGE_BASE_PROMPT =
22
+ "From knowledge base:\n{knowledgeBaseData}\nanswer user's question using this information.";
23
+
24
+ export class DefaultAIEmployeeManager implements AIEmployeeManager {
25
+ private readonly employees = new Registry<AIEmployeeOptions>();
26
+ private readonly provideCollectionManager: () => { collectionManager: SequelizeCollectionManager };
27
+ private mode = 'memory';
28
+
29
+ constructor(private readonly app: any) {
30
+ this.provideCollectionManager = () => app.mainDataSource;
31
+ }
32
+
33
+ async init() {
34
+ if (this.mode === 'memory') {
35
+ await this.persistence();
36
+ this.mode = 'database';
37
+ }
38
+ }
39
+
40
+ async getEmployee(username: string): Promise<AIEmployeeEntry> {
41
+ return (await this.aiEmployeesModel.findOne({ where: { username } }))?.toJSON() as AIEmployeeEntry;
42
+ }
43
+
44
+ async listEmployees(filter: AIEmployeeFilter = {}): Promise<AIEmployeeEntry[]> {
45
+ const where = {};
46
+ if (filter.builtIn != null) {
47
+ where['builtIn'] = filter.builtIn;
48
+ }
49
+ if (filter.username) {
50
+ where['username'] = {
51
+ [Op.substring]: filter.username,
52
+ };
53
+ }
54
+ return (await this.aiEmployeesModel.findAll({ where })).map((it) => it.toJSON() as AIEmployeeEntry);
55
+ }
56
+
57
+ async registerEmployee(options: AIEmployeeOptions): Promise<void> {
58
+ if (this.mode === 'memory') {
59
+ return this.registerEmployeeInMemory(options);
60
+ }
61
+ return this.registerEmployeeInDatabase(options);
62
+ }
63
+
64
+ async persistence(): Promise<void> {
65
+ const employees = [...this.employees.getValues()];
66
+ for (const employee of employees) {
67
+ await this.registerEmployeeInDatabase(employee);
68
+ }
69
+ }
70
+
71
+ private registerEmployeeInMemory(options: AIEmployeeOptions): void {
72
+ this.employees.register(options.username, options);
73
+ }
74
+
75
+ private async registerEmployeeInDatabase(options: AIEmployeeOptions): Promise<void> {
76
+ const employee = options;
77
+ await this.sequelize.transaction(async (transaction) => {
78
+ const existed = await this.aiEmployeesModel.findOne({ where: { username: employee.username }, transaction });
79
+ if (!existed) {
80
+ await this.aiEmployeesModel.create(
81
+ {
82
+ username: employee.username,
83
+ nickname: employee.nickname,
84
+ position: employee.position,
85
+ avatar: employee.avatar,
86
+ bio: employee.bio,
87
+ greeting: employee.greeting,
88
+ about: null,
89
+ defaultPrompt: employee.systemPrompt,
90
+ skillSettings: {
91
+ skills: employee.skills,
92
+ tools: employee.tools,
93
+ },
94
+ enableKnowledgeBase: false,
95
+ knowledgeBase: DEFAULT_KNOWLEDGE_BASE,
96
+ knowledgeBasePrompt: DEFAULT_KNOWLEDGE_BASE_PROMPT,
97
+ enabled: true,
98
+ builtIn: true,
99
+ sort: employee.sort,
100
+ },
101
+ { transaction },
102
+ );
103
+ return;
104
+ }
105
+
106
+ const current = existed.toJSON() as AIEmployeeEntry;
107
+ let { tools } = current?.skillSettings ?? { tools: [] };
108
+ tools = tools?.length ? tools.filter((s) => s.name?.startsWith('workflowCaller-')) : [];
109
+ const mergedTools = new Set([...tools, ...employee.tools]);
110
+ const values: Record<string, unknown> = {
111
+ nickname: employee.nickname ?? current.nickname,
112
+ position: employee.position ?? current.position,
113
+ avatar: employee.avatar ?? current.avatar,
114
+ bio: employee.bio ?? current.bio,
115
+ greeting: employee.greeting ?? current.greeting,
116
+ defaultPrompt: employee.systemPrompt,
117
+ skillSettings: {
118
+ skills: [...employee.skills],
119
+ tools: [...mergedTools],
120
+ },
121
+ sort: employee.sort,
122
+ };
123
+ await existed.update(values, { transaction });
124
+ });
125
+ }
126
+
127
+ private get aiEmployeesCollection() {
128
+ return this.collectionManager.getCollection('aiEmployees');
129
+ }
130
+
131
+ private get aiEmployeesModel() {
132
+ return this.aiEmployeesCollection?.model;
133
+ }
134
+
135
+ private get sequelize() {
136
+ return this.collectionManager.db.sequelize;
137
+ }
138
+
139
+ private get collectionManager() {
140
+ return this.provideCollectionManager().collectionManager;
141
+ }
142
+ }
143
+
144
+ export function defineAIEmployee(options: AIEmployeeOptions) {
145
+ return options;
146
+ }
147
+
148
+ export * from './types';
@@ -0,0 +1,63 @@
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 type AIEmployeeLocalizedProfile = {
11
+ avatar?: string;
12
+ nickname?: string;
13
+ position?: string;
14
+ bio?: string;
15
+ greeting?: string;
16
+ about?: string;
17
+ };
18
+
19
+ export type AIEmployeeKnowledgeBase = {
20
+ topK: number;
21
+ score: string;
22
+ knowledgeBaseIds: string[];
23
+ };
24
+
25
+ export type AIEmployeeToolSetting = {
26
+ name: string;
27
+ autoCall?: boolean;
28
+ };
29
+
30
+ export type AIEmployeeOptions = {
31
+ username: string;
32
+ description?: string;
33
+ skills?: string[];
34
+ tools?: AIEmployeeToolSetting[];
35
+ avatar?: string;
36
+ nickname?: string;
37
+ position?: string;
38
+ bio?: string;
39
+ greeting?: string;
40
+ systemPrompt?: string | null;
41
+ sort?: number;
42
+ };
43
+
44
+ export type AIEmployeeEntry = Omit<AIEmployeeOptions, 'skills' | 'tools' | 'systemPrompt'> & {
45
+ about?: string;
46
+ defaultPrompt?: string;
47
+ skillSettings: {
48
+ skills: string[];
49
+ tools: AIEmployeeToolSetting[];
50
+ };
51
+ };
52
+
53
+ export type AIEmployeeFilter = {
54
+ builtIn?: boolean;
55
+ username?: string;
56
+ };
57
+
58
+ export interface AIEmployeeManager {
59
+ init(): Promise<void>;
60
+ getEmployee(username: string): Promise<AIEmployeeEntry>;
61
+ listEmployees(filter?: AIEmployeeFilter): Promise<AIEmployeeEntry[]>;
62
+ registerEmployee(options: AIEmployeeOptions): Promise<void>;
63
+ }
package/src/ai-manager.ts CHANGED
@@ -9,13 +9,25 @@
9
9
 
10
10
  import { DocumentManager } from './document-manager';
11
11
  import { DefaultToolsManager, ToolsManager } from './tools-manager';
12
+ import { DefaultSkillsManager, SkillsManager } from './skills-manager';
13
+ import { AIEmployeeManager, DefaultAIEmployeeManager } from './ai-employee-manager';
14
+ import { DefaultMCPManager, MCPManager } from './mcp-manager';
15
+ import { McpToolsManager } from './mcp-tools-manager';
12
16
 
13
17
  export class AIManager {
14
18
  documentManager: DocumentManager;
15
19
  toolsManager: ToolsManager;
20
+ skillsManager: SkillsManager;
21
+ employeeManager: AIEmployeeManager;
22
+ mcpManager: MCPManager;
23
+ mcpToolsManager: McpToolsManager;
16
24
 
17
25
  constructor(protected readonly app: any) {
18
26
  this.documentManager = new DocumentManager();
19
27
  this.toolsManager = new DefaultToolsManager();
28
+ this.skillsManager = new DefaultSkillsManager(app);
29
+ this.employeeManager = new DefaultAIEmployeeManager(app);
30
+ this.mcpManager = new DefaultMCPManager(app);
31
+ this.mcpToolsManager = new McpToolsManager();
20
32
  }
21
33
  }
@@ -0,0 +1,57 @@
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 { Document } from '@langchain/core/documents';
11
+ import { Worker } from 'node:worker_threads';
12
+ import path from 'node:path';
13
+
14
+ export const loadByWorker = async (extname: string, blob: Blob): Promise<Document[]> => {
15
+ const buffer = Buffer.from(await blob.arrayBuffer());
16
+ const isTsRuntime = __filename.endsWith('.ts');
17
+ const workerPath = path.join(__dirname, `loader.worker.${isTsRuntime ? 'ts' : 'js'}`);
18
+ const worker = new Worker(workerPath, {
19
+ execArgv: isTsRuntime ? ['--require', 'tsx/cjs'] : undefined,
20
+ });
21
+ return new Promise<Document[]>((resolve, reject) => {
22
+ let settled = false;
23
+ const close = (error?: Error, result?: Document[]) => {
24
+ if (settled) {
25
+ return;
26
+ }
27
+ settled = true;
28
+ if (error) {
29
+ reject(error);
30
+ return;
31
+ }
32
+ resolve(result || []);
33
+ };
34
+
35
+ worker.once('message', (payload: { documents?: Document[]; error?: string }) => {
36
+ if (payload?.error) {
37
+ close(new Error(payload.error));
38
+ return;
39
+ }
40
+ close(undefined, payload?.documents || []);
41
+ });
42
+ worker.once('error', (error) => close(error));
43
+ worker.once('exit', (code) => {
44
+ if (!settled && code !== 0) {
45
+ close(new Error(`Document loader worker exited with code ${code}`));
46
+ }
47
+ });
48
+
49
+ worker.postMessage({
50
+ extname,
51
+ mimeType: blob.type,
52
+ buffer: Uint8Array.from(buffer),
53
+ });
54
+ }).finally(() => {
55
+ worker.terminate().catch(() => undefined);
56
+ });
57
+ };
@@ -0,0 +1,100 @@
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 { PDFLoader } from '@langchain/community/document_loaders/fs/pdf';
11
+ import { PPTXLoader } from '@langchain/community/document_loaders/fs/pptx';
12
+ import { DocxLoader } from '@langchain/community/document_loaders/fs/docx';
13
+ import type { Document } from '@langchain/core/documents';
14
+ import { parentPort } from 'node:worker_threads';
15
+ import { TextLoader } from './vendor/langchain/document_loaders/fs/text';
16
+ import { CSVLoader } from '@langchain/community/document_loaders/fs/csv';
17
+ import { loadXlsx } from './xlsx';
18
+
19
+ type ParsePayload = {
20
+ extname: string;
21
+ mimeType?: string;
22
+ buffer: Uint8Array;
23
+ };
24
+
25
+ type WorkerResponse = {
26
+ documents?: Array<Pick<Document, 'pageContent' | 'metadata' | 'id'>>;
27
+ error?: string;
28
+ };
29
+
30
+ const loadPdf = async (blob: Blob): Promise<Document[]> => {
31
+ const loader = new PDFLoader(blob);
32
+ return loader.load();
33
+ };
34
+
35
+ const loadDoc = async (blob: Blob, type: 'docx' | 'doc'): Promise<Document[]> => {
36
+ const loader = new DocxLoader(blob, { type });
37
+ return loader.load();
38
+ };
39
+
40
+ const loadPpt = async (blob: Blob): Promise<Document[]> => {
41
+ const loader = new PPTXLoader(blob);
42
+ return loader.load();
43
+ };
44
+
45
+ const loadTxt = async (blob: Blob): Promise<Document[]> => {
46
+ const loader = new TextLoader(blob);
47
+ return loader.load();
48
+ };
49
+
50
+ const loadCsv = async (blob: Blob): Promise<Document[]> => {
51
+ const loader = new CSVLoader(blob);
52
+ return loader.load();
53
+ };
54
+
55
+ const loadByExtname = async (payload: ParsePayload): Promise<Document[]> => {
56
+ // @ts-ignore
57
+ const blob = new Blob([Buffer.from(payload.buffer)], { type: payload.mimeType ?? 'application/octet-stream' });
58
+
59
+ switch (payload.extname) {
60
+ case '.pdf':
61
+ return loadPdf(blob);
62
+ case '.ppt':
63
+ case '.pptx':
64
+ return loadPpt(blob);
65
+ case '.doc':
66
+ return loadDoc(blob, 'doc');
67
+ case '.docx':
68
+ return loadDoc(blob, 'docx');
69
+ case '.csv':
70
+ return loadCsv(blob);
71
+ case '.xls':
72
+ case '.xlsx':
73
+ return loadXlsx(blob);
74
+ case '.json':
75
+ case '.md':
76
+ case '.txt':
77
+ return loadTxt(blob);
78
+ default:
79
+ return [];
80
+ }
81
+ };
82
+
83
+ parentPort?.on('message', async (payload: ParsePayload) => {
84
+ try {
85
+ const documents = await loadByExtname(payload);
86
+ const response: WorkerResponse = {
87
+ documents: documents.map((doc) => ({
88
+ pageContent: doc.pageContent,
89
+ metadata: doc.metadata,
90
+ id: doc.id,
91
+ })),
92
+ };
93
+ parentPort?.postMessage(response);
94
+ } catch (error) {
95
+ const response: WorkerResponse = {
96
+ error: String(error?.stack || error),
97
+ };
98
+ parentPort?.postMessage(response);
99
+ }
100
+ });
@@ -0,0 +1,72 @@
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 { Document } from '@langchain/core/documents';
11
+ import { getEnv } from '@langchain/core/utils/env';
12
+ import { BaseDocumentLoader } from '@langchain/core/document_loaders/base';
13
+
14
+ export class TextLoader extends BaseDocumentLoader {
15
+ private filePathOrBlob: any;
16
+
17
+ constructor(filePathOrBlob) {
18
+ super();
19
+ this.filePathOrBlob = filePathOrBlob;
20
+ }
21
+
22
+ async parse(raw) {
23
+ return [raw];
24
+ }
25
+
26
+ async load() {
27
+ let text;
28
+ let metadata;
29
+
30
+ if (typeof this.filePathOrBlob === 'string') {
31
+ const { readFile } = await TextLoader.imports();
32
+ text = await readFile(this.filePathOrBlob, 'utf8');
33
+ metadata = { source: this.filePathOrBlob };
34
+ } else {
35
+ text = await this.filePathOrBlob.text();
36
+ metadata = { source: 'blob', blobType: this.filePathOrBlob.type };
37
+ }
38
+
39
+ const parsed = await this.parse(text);
40
+ parsed.forEach((pageContent, i) => {
41
+ if (typeof pageContent !== 'string') {
42
+ throw new Error(`Expected string, at position ${i} got ${typeof pageContent}`);
43
+ }
44
+ });
45
+
46
+ return parsed.map(
47
+ (pageContent, i) =>
48
+ new Document({
49
+ pageContent,
50
+ metadata:
51
+ parsed.length === 1
52
+ ? metadata
53
+ : {
54
+ ...metadata,
55
+ line: i + 1,
56
+ },
57
+ }),
58
+ );
59
+ }
60
+
61
+ static async imports() {
62
+ try {
63
+ const { readFile } = await import('node:fs/promises');
64
+ return { readFile };
65
+ } catch (e) {
66
+ console.error(e);
67
+ throw new Error(
68
+ `Failed to load fs/promises. TextLoader available only on environment 'node'. It appears you are running environment '${getEnv()}'. See https://<link to docs> for alternatives.`,
69
+ );
70
+ }
71
+ }
72
+ }
@@ -0,0 +1,82 @@
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 { Document } from '@langchain/core/documents';
11
+ import * as XLSX from 'xlsx';
12
+
13
+ const normalizeCellValue = (value: unknown): string => {
14
+ if (value === undefined || value === null) {
15
+ return '';
16
+ }
17
+
18
+ return String(value);
19
+ };
20
+
21
+ const trimTrailingEmptyCells = (row: unknown[]): unknown[] => {
22
+ let end = row.length;
23
+
24
+ while (end > 0 && normalizeCellValue(row[end - 1]).trim() === '') {
25
+ end -= 1;
26
+ }
27
+
28
+ return row.slice(0, end);
29
+ };
30
+
31
+ const sheetToLines = (sheet: XLSX.WorkSheet): string[] => {
32
+ const rows = XLSX.utils.sheet_to_json(sheet, {
33
+ header: 1,
34
+ raw: false,
35
+ defval: '',
36
+ blankrows: false,
37
+ }) as unknown[][];
38
+
39
+ return rows
40
+ .map((row) => trimTrailingEmptyCells(Array.isArray(row) ? row : []))
41
+ .filter((row) => row.length > 0)
42
+ .map((row) => row.map((cell) => normalizeCellValue(cell)).join('\t'))
43
+ .filter((line) => line.trim().length > 0);
44
+ };
45
+
46
+ export const loadXlsx = async (blob: Blob): Promise<Document[]> => {
47
+ const buffer = await blob.arrayBuffer();
48
+ const workbook = XLSX.read(buffer, {
49
+ type: 'array',
50
+ cellText: true,
51
+ });
52
+
53
+ const documents: Document[] = [];
54
+
55
+ workbook.SheetNames.forEach((sheetName, index) => {
56
+ const sheet = workbook.Sheets[sheetName];
57
+
58
+ if (!sheet) {
59
+ return;
60
+ }
61
+
62
+ const lines = sheetToLines(sheet);
63
+
64
+ if (!lines.length) {
65
+ return;
66
+ }
67
+
68
+ documents.push(
69
+ new Document({
70
+ pageContent: [`Sheet: ${sheetName}`, ...lines].join('\n'),
71
+ metadata: {
72
+ source: 'blob',
73
+ blobType: blob.type,
74
+ sheetName,
75
+ sheetIndex: index,
76
+ },
77
+ }),
78
+ );
79
+ });
80
+
81
+ return documents;
82
+ };
package/src/index.ts CHANGED
@@ -8,6 +8,11 @@
8
8
  */
9
9
 
10
10
  export * from './ai-manager';
11
+ export * from './ai-employee-manager';
11
12
  export * from './document-manager';
13
+ export * from './mcp-tools-manager';
12
14
  export * from './tools-manager';
15
+ export * from './skills-manager';
16
+ export * from './mcp-manager';
13
17
  export * from './loader';
18
+ export * from './document-loader';