@nocobase/plugin-ai 2.1.0-alpha.20 → 2.1.0-alpha.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.
- package/dist/ai/tools/knowledge-base-retrieve.d.ts +10 -0
- package/dist/ai/tools/knowledge-base-retrieve.js +94 -0
- package/dist/client/486.77c26e2e7f8daf28.js +10 -0
- package/dist/client/646.ef9d7c2ea8641044.js +10 -0
- package/dist/client/ai-employees/chatbox/AIEmployeeSwitch.d.ts +3 -1
- package/dist/client/ai-employees/chatbox/HintMessageHeader.d.ts +10 -0
- package/dist/client/ai-employees/chatbox/ModelSwitcher.d.ts +3 -1
- package/dist/client/ai-employees/chatbox/SearchSwitch.d.ts +3 -1
- package/dist/client/ai-employees/chatbox/Upload.d.ts +3 -1
- package/dist/client/ai-employees/chatbox/conversations/ConversationsList.d.ts +27 -0
- package/dist/client/ai-employees/chatbox/conversations/WorkflowTasksList.d.ts +31 -0
- package/dist/client/ai-employees/chatbox/conversations/common.d.ts +43 -0
- package/dist/{server/document-loader/loader.worker.d.ts → client/ai-employees/chatbox/conversations/index.d.ts} +1 -1
- package/dist/client/ai-employees/chatbox/hooks/useWorkflowTasks.d.ts +24 -0
- package/dist/client/ai-employees/chatbox/stores/chat-box.d.ts +8 -0
- package/dist/client/ai-employees/chatbox/stores/chat-conversations.d.ts +4 -0
- package/dist/client/ai-employees/chatbox/stores/workflow-tasks.d.ts +41 -0
- package/dist/client/ai-employees/workflow-tasks/tools/index.d.ts +10 -0
- package/dist/client/ai-employees/workflow-tasks/ui/WorkflowTaskOutputCard.d.ts +11 -0
- package/dist/client/components/ListCollapse.d.ts +28 -0
- package/dist/client/index.js +7 -7
- package/dist/client/repositories/AIConfigRepository.d.ts +3 -2
- package/dist/client/workflow/nodes/employee/components/AIEmployeeSelect.d.ts +10 -0
- package/dist/client/workflow/nodes/employee/components/assigness.d.ts +11 -0
- package/dist/client/workflow/nodes/employee/components/file-inputs.d.ts +10 -0
- package/dist/client/workflow/nodes/employee/components/message-inputs.d.ts +12 -0
- package/dist/client/workflow/nodes/employee/components/model-options.d.ts +10 -0
- package/dist/client/workflow/nodes/employee/components/skill-settings.d.ts +10 -0
- package/dist/client/workflow/nodes/employee/components/structured-output.d.ts +10 -0
- package/dist/client/workflow/nodes/employee/components/users-select.d.ts +15 -0
- package/dist/client/workflow/nodes/employee/components/web-search-options.d.ts +10 -0
- package/dist/client/workflow/nodes/employee/configuration.d.ts +12 -0
- package/dist/client/workflow/nodes/employee/flow-models/feedback.d.ts +13 -0
- package/dist/client/workflow/nodes/employee/flow-models/task.d.ts +13 -0
- package/dist/client/workflow/nodes/employee/index.d.ts +7 -31
- package/dist/externalVersion.js +14 -15
- package/dist/locale/en-US.json +44 -1
- package/dist/locale/zh-CN.json +45 -1
- package/dist/node_modules/fast-glob/out/index.js +8 -8
- package/dist/node_modules/fast-glob/package.json +1 -1
- package/dist/node_modules/flexsearch/dist/flexsearch.bundle.min.js +2 -2
- package/dist/node_modules/flexsearch/package.json +1 -1
- package/dist/node_modules/fs-extra/lib/index.js +1 -1
- package/dist/node_modules/fs-extra/package.json +1 -1
- package/dist/node_modules/nodejs-snowflake/nodejs_snowflake.js +1 -1
- package/dist/node_modules/nodejs-snowflake/package.json +1 -1
- package/dist/node_modules/openai/index.js +1 -1
- package/dist/node_modules/openai/package.json +1 -1
- package/dist/node_modules/zod/index.cjs +1 -1
- package/dist/node_modules/zod/package.json +1 -1
- package/dist/server/ai-employees/ai-conversations.d.ts +10 -4
- package/dist/server/ai-employees/ai-conversations.js +52 -14
- package/dist/server/ai-employees/ai-employee.d.ts +8 -10
- package/dist/server/ai-employees/ai-employee.js +130 -145
- package/dist/server/ai-employees/ai-knowledge-base.d.ts +28 -0
- package/dist/server/ai-employees/ai-knowledge-base.js +167 -0
- package/dist/server/ai-employees/middleware/index.d.ts +1 -0
- package/dist/server/ai-employees/middleware/index.js +3 -1
- package/dist/server/ai-employees/middleware/workflow-history.d.ts +22 -0
- package/dist/server/ai-employees/middleware/workflow-history.js +173 -0
- package/dist/server/collections/ai-conversations.js +12 -0
- package/dist/server/collections/ai-workflow-tasks.d.ts +10 -0
- package/dist/server/collections/ai-workflow-tasks.js +112 -0
- package/dist/server/collections/users-ai-workflow-tasks.d.ts +10 -0
- package/dist/server/collections/users-ai-workflow-tasks.js +44 -0
- package/dist/server/document-loader/constants.d.ts +1 -2
- package/dist/server/document-loader/constants.js +6 -1
- package/dist/server/document-loader/loader.d.ts +0 -1
- package/dist/server/document-loader/loader.js +2 -55
- package/dist/server/document-loader/types.d.ts +0 -1
- package/dist/server/llm-providers/provider.js +1 -1
- package/dist/server/manager/built-in-manager.js +5 -4
- package/dist/server/plugin.d.ts +2 -0
- package/dist/server/plugin.js +10 -0
- package/dist/server/resource/aiConversations.js +29 -35
- package/dist/server/resource/aiWorkflowTasks.d.ts +12 -0
- package/dist/server/resource/aiWorkflowTasks.js +290 -0
- package/dist/server/utils.js +3 -2
- package/dist/server/workflow/nodes/employee/files.d.ts +16 -0
- package/dist/server/workflow/nodes/employee/files.js +125 -0
- package/dist/server/workflow/nodes/employee/handler.d.ts +11 -0
- package/dist/server/workflow/nodes/employee/handler.js +107 -0
- package/dist/server/workflow/nodes/employee/index.d.ts +17 -0
- package/dist/server/workflow/nodes/employee/index.js +391 -0
- package/dist/server/workflow/nodes/employee/tools.d.ts +12 -0
- package/dist/server/workflow/nodes/employee/tools.js +133 -0
- package/dist/server/workflow/nodes/employee/types.d.ts +35 -0
- package/dist/server/workflow/nodes/employee/types.js +24 -0
- package/dist/server/workflow/utils.d.ts +14 -0
- package/dist/server/workflow/utils.js +111 -0
- package/package.json +2 -2
- package/dist/client/30.4f30511a3059c422.js +0 -10
- package/dist/server/document-loader/loader.worker.js +0 -68
- package/dist/server/document-loader/vendor/langchain/document_loaders/fs/text.d.ts +0 -20
- package/dist/server/document-loader/vendor/langchain/document_loaders/fs/text.js +0 -96
- /package/dist/client/ai-employees/chatbox/{Conversations.d.ts → conversations/Conversations.d.ts} +0 -0
|
@@ -0,0 +1,107 @@
|
|
|
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
|
+
var __defProp = Object.defineProperty;
|
|
11
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
12
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
13
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
14
|
+
var __export = (target, all) => {
|
|
15
|
+
for (var name in all)
|
|
16
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
17
|
+
};
|
|
18
|
+
var __copyProps = (to, from, except, desc) => {
|
|
19
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
20
|
+
for (let key of __getOwnPropNames(from))
|
|
21
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
22
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
23
|
+
}
|
|
24
|
+
return to;
|
|
25
|
+
};
|
|
26
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
27
|
+
var handler_exports = {};
|
|
28
|
+
__export(handler_exports, {
|
|
29
|
+
registerAIEmployeeTaskNotification: () => registerAIEmployeeTaskNotification,
|
|
30
|
+
registerOnJobAbortedHandler: () => registerOnJobAbortedHandler
|
|
31
|
+
});
|
|
32
|
+
module.exports = __toCommonJS(handler_exports);
|
|
33
|
+
var import_plugin_workflow = require("@nocobase/plugin-workflow");
|
|
34
|
+
const registerOnJobAbortedHandler = (plugin) => {
|
|
35
|
+
plugin.db.on("jobs.afterBulkUpdate", async (options) => {
|
|
36
|
+
const { model, attributes, where, transaction } = options;
|
|
37
|
+
if (attributes.status !== import_plugin_workflow.JOB_STATUS.ABORTED) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const jobs = await model.findAll({ where, transaction });
|
|
41
|
+
const aiWorkflowTasks = await plugin.db.getRepository("aiWorkflowTasks").find({
|
|
42
|
+
filter: {
|
|
43
|
+
jobId: jobs.map((it) => it.id)
|
|
44
|
+
},
|
|
45
|
+
transaction
|
|
46
|
+
});
|
|
47
|
+
if (!aiWorkflowTasks.length) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
for (const aiWorkflowTask of aiWorkflowTasks) {
|
|
51
|
+
await plugin.db.getRepository("aiWorkflowTasks").update({
|
|
52
|
+
values: { status: "aborted" },
|
|
53
|
+
filter: {
|
|
54
|
+
id: aiWorkflowTask.id
|
|
55
|
+
},
|
|
56
|
+
transaction
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
const registerAIEmployeeTaskNotification = (plugin) => {
|
|
62
|
+
plugin.db.on("aiWorkflowTasks.beforeSave", async (model, options) => {
|
|
63
|
+
var _a;
|
|
64
|
+
if (!model.isNewRecord && !model.changed("status")) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const values = model.toJSON();
|
|
68
|
+
(_a = options.transaction) == null ? void 0 : _a.afterCommit(async () => {
|
|
69
|
+
const assignees = await plugin.db.getRepository("usersAiWorkflowTasks").find({
|
|
70
|
+
filter: {
|
|
71
|
+
aiWorkflowTaskId: values.id
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
if (!(assignees == null ? void 0 : assignees.length)) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (values.status === "pending_acceptance") {
|
|
78
|
+
await plugin.db.getRepository("usersAiWorkflowTasks").update({
|
|
79
|
+
values: {
|
|
80
|
+
read: false
|
|
81
|
+
},
|
|
82
|
+
filter: {
|
|
83
|
+
aiWorkflowTaskId: values.id
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
for (const assignee of assignees) {
|
|
88
|
+
plugin.app.emit("ws:sendToUser", {
|
|
89
|
+
userId: assignee.userId,
|
|
90
|
+
message: {
|
|
91
|
+
type: "ai-employee-tasks:status",
|
|
92
|
+
payload: {
|
|
93
|
+
taskId: values.id,
|
|
94
|
+
sessionId: values.sessionId,
|
|
95
|
+
status: values.status
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
104
|
+
0 && (module.exports = {
|
|
105
|
+
registerAIEmployeeTaskNotification,
|
|
106
|
+
registerOnJobAbortedHandler
|
|
107
|
+
});
|
|
@@ -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
|
+
import { FlowNodeModel, Instruction, Processor } from '@nocobase/plugin-workflow';
|
|
10
|
+
export declare class AIEmployeeInstruction extends Instruction {
|
|
11
|
+
run(node: FlowNodeModel, input: any, processor: Processor): Promise<void>;
|
|
12
|
+
resume(node: FlowNodeModel, job: any, processor: Processor): any;
|
|
13
|
+
private createWorkflowTask;
|
|
14
|
+
private checkApproval;
|
|
15
|
+
}
|
|
16
|
+
export * from './handler';
|
|
17
|
+
export * from './tools';
|
|
@@ -0,0 +1,391 @@
|
|
|
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
|
+
var __create = Object.create;
|
|
11
|
+
var __defProp = Object.defineProperty;
|
|
12
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
13
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
14
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
15
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
16
|
+
var __export = (target, all) => {
|
|
17
|
+
for (var name in all)
|
|
18
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
19
|
+
};
|
|
20
|
+
var __copyProps = (to, from, except, desc) => {
|
|
21
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
22
|
+
for (let key of __getOwnPropNames(from))
|
|
23
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
24
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
25
|
+
}
|
|
26
|
+
return to;
|
|
27
|
+
};
|
|
28
|
+
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
29
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
30
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
31
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
32
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
33
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
34
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
35
|
+
mod
|
|
36
|
+
));
|
|
37
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
38
|
+
var employee_exports = {};
|
|
39
|
+
__export(employee_exports, {
|
|
40
|
+
AIEmployeeInstruction: () => AIEmployeeInstruction
|
|
41
|
+
});
|
|
42
|
+
module.exports = __toCommonJS(employee_exports);
|
|
43
|
+
var import_plugin_workflow = require("@nocobase/plugin-workflow");
|
|
44
|
+
var import_plugin = __toESM(require("../../../plugin"));
|
|
45
|
+
var import_ai_employee = require("../../../ai-employees/ai-employee");
|
|
46
|
+
var import_files = require("./files");
|
|
47
|
+
var import_utils = require("@nocobase/utils");
|
|
48
|
+
__reExport(employee_exports, require("./handler"), module.exports);
|
|
49
|
+
__reExport(employee_exports, require("./tools"), module.exports);
|
|
50
|
+
class AIEmployeeInstruction extends import_plugin_workflow.Instruction {
|
|
51
|
+
async run(node, input, processor) {
|
|
52
|
+
var _a, _b, _c, _d, _e;
|
|
53
|
+
const {
|
|
54
|
+
username,
|
|
55
|
+
message,
|
|
56
|
+
skillSettings,
|
|
57
|
+
webSearch,
|
|
58
|
+
model,
|
|
59
|
+
requiresApproval = "no_required",
|
|
60
|
+
userId,
|
|
61
|
+
files
|
|
62
|
+
} = processor.getParsedValue(node.config, node.id);
|
|
63
|
+
const toolName = "aiEmployeeWorkflowTaskOutput";
|
|
64
|
+
const workflowSystemPrompt = `
|
|
65
|
+
You are operating inside a workflow.
|
|
66
|
+
Your job is to complete the workflow task and return the final outcome to the workflow, not to reply freely as a normal assistant.
|
|
67
|
+
Use normal assistant messages only for reasoning that leads to tool calls; do not place the final outcome in a normal assistant message.
|
|
68
|
+
When the task is ready to be submitted, you must call **${toolName}** to return it to the workflow.
|
|
69
|
+
Do not treat **${toolName}** as optional, and do not finish the task without calling it.
|
|
70
|
+
`.trim();
|
|
71
|
+
const systemMessage = `${workflowSystemPrompt}
|
|
72
|
+
|
|
73
|
+
${typeof message.system === "object" ? JSON.stringify(message.system) : message.system}`;
|
|
74
|
+
const userMessage = `Current workflow task description:
|
|
75
|
+
|
|
76
|
+
${typeof message.user === "object" ? JSON.stringify(message.user) : message.user}`;
|
|
77
|
+
const { id } = processor.saveJob({
|
|
78
|
+
status: import_plugin_workflow.JOB_STATUS.PENDING,
|
|
79
|
+
nodeId: node.id,
|
|
80
|
+
nodeKey: node.key,
|
|
81
|
+
upstreamId: (input == null ? void 0 : input.id) ?? null
|
|
82
|
+
});
|
|
83
|
+
await processor.exit();
|
|
84
|
+
try {
|
|
85
|
+
const { conversation, aiWorkflowTasks } = await this.createWorkflowTask({
|
|
86
|
+
username,
|
|
87
|
+
userMessage,
|
|
88
|
+
systemMessage,
|
|
89
|
+
skillSettings,
|
|
90
|
+
requiresApproval,
|
|
91
|
+
toolName,
|
|
92
|
+
node,
|
|
93
|
+
processor,
|
|
94
|
+
jobId: id
|
|
95
|
+
});
|
|
96
|
+
let currentRoles = (_a = input == null ? void 0 : input.result) == null ? void 0 : _a.roleName;
|
|
97
|
+
if (!currentRoles) {
|
|
98
|
+
const defaultRole = await this.workflow.db.getRepository("rolesUsers").findOne({
|
|
99
|
+
filter: {
|
|
100
|
+
userId: ((_c = (_b = input == null ? void 0 : input.result) == null ? void 0 : _b.user) == null ? void 0 : _c.id) ?? userId,
|
|
101
|
+
default: true
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
currentRoles = defaultRole == null ? void 0 : defaultRole.roleName;
|
|
105
|
+
}
|
|
106
|
+
const employee = await this.workflow.db.getRepository("aiEmployees").findOne({
|
|
107
|
+
filter: {
|
|
108
|
+
username
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
const aiEmployee = new import_ai_employee.AIEmployee({
|
|
112
|
+
ctx: {
|
|
113
|
+
app: this.workflow.app,
|
|
114
|
+
db: this.workflow.app.db,
|
|
115
|
+
log: this.workflow.app.log,
|
|
116
|
+
logger: this.workflow.app.log,
|
|
117
|
+
state: { currentRoles },
|
|
118
|
+
auth: {
|
|
119
|
+
user: {
|
|
120
|
+
id: ((_e = (_d = input == null ? void 0 : input.result) == null ? void 0 : _d.user) == null ? void 0 : _e.id) ?? userId
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
action: {
|
|
124
|
+
params: {
|
|
125
|
+
values: {
|
|
126
|
+
sessionId: conversation.sessionId,
|
|
127
|
+
model
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
employee,
|
|
133
|
+
sessionId: conversation.sessionId,
|
|
134
|
+
systemMessage,
|
|
135
|
+
skillSettings,
|
|
136
|
+
webSearch,
|
|
137
|
+
model,
|
|
138
|
+
tools: [{ name: toolName }]
|
|
139
|
+
});
|
|
140
|
+
const attachmentPart = {};
|
|
141
|
+
if (files == null ? void 0 : files.length) {
|
|
142
|
+
const { resolveAttachments, resolveUrls } = import_files.Files.resolvers(this.workflow, attachmentPart);
|
|
143
|
+
await resolveAttachments(files);
|
|
144
|
+
await resolveUrls(files);
|
|
145
|
+
}
|
|
146
|
+
let result;
|
|
147
|
+
let isToolInvoke = false;
|
|
148
|
+
let retry = 0;
|
|
149
|
+
do {
|
|
150
|
+
const userMessages = [
|
|
151
|
+
{
|
|
152
|
+
role: "user",
|
|
153
|
+
content: {
|
|
154
|
+
type: "text",
|
|
155
|
+
content: userMessage
|
|
156
|
+
},
|
|
157
|
+
...attachmentPart
|
|
158
|
+
}
|
|
159
|
+
];
|
|
160
|
+
if (retry > 0) {
|
|
161
|
+
if (retry < 2) {
|
|
162
|
+
const firstUserMessage = await this.workflow.db.getRepository("aiConversations.messages", conversation.sessionId).findOne({
|
|
163
|
+
filter: {
|
|
164
|
+
role: "user"
|
|
165
|
+
},
|
|
166
|
+
sort: ["messageId"]
|
|
167
|
+
});
|
|
168
|
+
const messageId = firstUserMessage == null ? void 0 : firstUserMessage.messageId;
|
|
169
|
+
result = await aiEmployee.invoke({ messageId, userMessages });
|
|
170
|
+
} else {
|
|
171
|
+
result = await aiEmployee.invoke({
|
|
172
|
+
userMessages: [
|
|
173
|
+
{
|
|
174
|
+
role: "user",
|
|
175
|
+
content: {
|
|
176
|
+
type: "text",
|
|
177
|
+
content: `You failed to call the required tool "aiEmployeeWorkflowTaskOutput" in your previous response.
|
|
178
|
+
Call "aiEmployeeWorkflowTaskOutput" now to submit the workflow outcome.
|
|
179
|
+
Do not send another normal assistant response without invoking it.
|
|
180
|
+
`
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
]
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
} else {
|
|
187
|
+
result = await aiEmployee.invoke({ userMessages });
|
|
188
|
+
}
|
|
189
|
+
isToolInvoke = result.messages.filter((it) => it.type === "ai").flatMap((it) => it.tool_calls).some((it) => it.name === toolName);
|
|
190
|
+
} while (!isToolInvoke && retry++ < 2);
|
|
191
|
+
if (!isToolInvoke) {
|
|
192
|
+
throw new Error("AI employee not do job correctly");
|
|
193
|
+
}
|
|
194
|
+
await this.checkApproval({ requiresApproval, conversation, aiWorkflowTasks, result, aiEmployee, toolName });
|
|
195
|
+
} catch (e) {
|
|
196
|
+
processor.logger.error(`ai employee invoke failed, ${e.message}`, {
|
|
197
|
+
node: node.id,
|
|
198
|
+
stack: e.stack,
|
|
199
|
+
chatOptions: node.config
|
|
200
|
+
});
|
|
201
|
+
const job = await this.workflow.app.db.getRepository("jobs").findOne({
|
|
202
|
+
filterByTk: id
|
|
203
|
+
});
|
|
204
|
+
job.set({
|
|
205
|
+
status: import_plugin_workflow.JOB_STATUS.ERROR,
|
|
206
|
+
result: e.message
|
|
207
|
+
});
|
|
208
|
+
await this.workflow.resume(job);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
resume(node, job, processor) {
|
|
212
|
+
return job;
|
|
213
|
+
}
|
|
214
|
+
async createWorkflowTask({
|
|
215
|
+
username,
|
|
216
|
+
userMessage,
|
|
217
|
+
systemMessage,
|
|
218
|
+
skillSettings,
|
|
219
|
+
requiresApproval,
|
|
220
|
+
toolName,
|
|
221
|
+
node,
|
|
222
|
+
processor,
|
|
223
|
+
jobId
|
|
224
|
+
}) {
|
|
225
|
+
const ai = this.workflow.app.pm.get(import_plugin.default);
|
|
226
|
+
return await this.workflow.db.sequelize.transaction(async (transaction) => {
|
|
227
|
+
var _a;
|
|
228
|
+
const conversation = await ai.aiConversationsManager.create({
|
|
229
|
+
aiEmployee: {
|
|
230
|
+
username
|
|
231
|
+
},
|
|
232
|
+
title: userMessage.slice(0, 30),
|
|
233
|
+
from: "sub-agent",
|
|
234
|
+
options: {
|
|
235
|
+
systemMessage,
|
|
236
|
+
skillSettings,
|
|
237
|
+
tools: [{ name: toolName }]
|
|
238
|
+
},
|
|
239
|
+
transaction,
|
|
240
|
+
category: "task"
|
|
241
|
+
});
|
|
242
|
+
const aiWorkflowTasks = await this.workflow.db.getRepository("aiWorkflowTasks").create({
|
|
243
|
+
values: {
|
|
244
|
+
id: this.workflow.app.snowflakeIdGenerator.generate(),
|
|
245
|
+
workflowTitle: (_a = processor.execution.workflow) == null ? void 0 : _a.title,
|
|
246
|
+
nodeTitle: node.title,
|
|
247
|
+
requiresApproval,
|
|
248
|
+
status: "processing",
|
|
249
|
+
sessionId: conversation.sessionId,
|
|
250
|
+
jobId,
|
|
251
|
+
executionId: processor.execution.id,
|
|
252
|
+
nodeId: node.id,
|
|
253
|
+
workflowId: node.workflowId
|
|
254
|
+
},
|
|
255
|
+
transaction
|
|
256
|
+
});
|
|
257
|
+
const userIds = await parseAssignees(node, processor);
|
|
258
|
+
if (userIds == null ? void 0 : userIds.length) {
|
|
259
|
+
await this.workflow.db.getRepository("usersAiWorkflowTasks").create({
|
|
260
|
+
values: userIds.map((userId) => ({
|
|
261
|
+
userId,
|
|
262
|
+
aiWorkflowTaskId: aiWorkflowTasks.id,
|
|
263
|
+
read: true
|
|
264
|
+
})),
|
|
265
|
+
transaction
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
return { conversation, aiWorkflowTasks };
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
async checkApproval({
|
|
272
|
+
requiresApproval,
|
|
273
|
+
conversation,
|
|
274
|
+
aiWorkflowTasks,
|
|
275
|
+
result,
|
|
276
|
+
aiEmployee,
|
|
277
|
+
toolName
|
|
278
|
+
}) {
|
|
279
|
+
var _a;
|
|
280
|
+
const ai = this.workflow.app.pm.get(import_plugin.default);
|
|
281
|
+
const aiToolMessage = await this.workflow.db.getRepository("aiToolMessages").findOne({
|
|
282
|
+
filter: {
|
|
283
|
+
sessionId: conversation.sessionId,
|
|
284
|
+
messageId: result.messageId
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
if ((aiToolMessage == null ? void 0 : aiToolMessage.invokeStatus) !== "interrupted") {
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
const aiMessage = await this.workflow.db.getRepository("aiMessages").findOne({
|
|
291
|
+
filter: {
|
|
292
|
+
messageId: result.messageId
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
const toolCalls = aiMessage == null ? void 0 : aiMessage.toolCalls;
|
|
296
|
+
if (!(toolCalls == null ? void 0 : toolCalls.length)) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
const toolCall = toolCalls.find((it) => it.name === toolName);
|
|
300
|
+
if (((_a = toolCall == null ? void 0 : toolCall.args) == null ? void 0 : _a.requiresApproval) === false) {
|
|
301
|
+
const [updated] = await this.workflow.db.getModel("aiToolMessages").update(
|
|
302
|
+
{ userDecision: { type: "approve" }, invokeStatus: "waiting" },
|
|
303
|
+
{
|
|
304
|
+
where: {
|
|
305
|
+
sessionId: conversation.sessionId,
|
|
306
|
+
messageId: result.messageId,
|
|
307
|
+
invokeStatus: "interrupted"
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
);
|
|
311
|
+
if (!updated) {
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
const userDecisions = await ai.aiConversationsManager.getUserDecisions(result.messageId);
|
|
315
|
+
await aiEmployee.invoke({ userDecisions });
|
|
316
|
+
await this.workflow.db.getRepository("aiWorkflowTasks").update({
|
|
317
|
+
values: { status: "approved" },
|
|
318
|
+
filter: {
|
|
319
|
+
id: aiWorkflowTasks.id,
|
|
320
|
+
status: {
|
|
321
|
+
$ne: "aborted"
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
} else if (requiresApproval !== "no_required") {
|
|
326
|
+
await this.workflow.db.getRepository("aiWorkflowTasks").update({
|
|
327
|
+
values: { status: "pending_acceptance" },
|
|
328
|
+
filter: {
|
|
329
|
+
id: aiWorkflowTasks.id,
|
|
330
|
+
status: {
|
|
331
|
+
$ne: "aborted"
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
async function parseAssignees(node, processor) {
|
|
339
|
+
const configAssignees = processor.getParsedValue(node.config.assignees ?? [], node.id).flat().filter(Boolean);
|
|
340
|
+
const assignees = [];
|
|
341
|
+
const seen = /* @__PURE__ */ new Set();
|
|
342
|
+
const addAssignee = (id) => {
|
|
343
|
+
if (!seen.has(id)) {
|
|
344
|
+
seen.add(id);
|
|
345
|
+
assignees.push(id);
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
const UserRepo = processor.options.plugin.app.db.getRepository("users");
|
|
349
|
+
const plainIds = configAssignees.flatMap(
|
|
350
|
+
(item) => Array.isArray(item) ? item : typeof item !== "object" ? [item] : []
|
|
351
|
+
);
|
|
352
|
+
const validIdSet = /* @__PURE__ */ new Set();
|
|
353
|
+
if (plainIds.length) {
|
|
354
|
+
const users = await UserRepo.find({
|
|
355
|
+
filter: { id: { $in: plainIds } },
|
|
356
|
+
fields: ["id"],
|
|
357
|
+
transaction: processor.mainTransaction
|
|
358
|
+
});
|
|
359
|
+
users.forEach((u) => validIdSet.add(u.id));
|
|
360
|
+
}
|
|
361
|
+
for (const item of configAssignees) {
|
|
362
|
+
if (Array.isArray(item)) {
|
|
363
|
+
for (const id of item) {
|
|
364
|
+
if (validIdSet.has(id)) {
|
|
365
|
+
addAssignee(id);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
} else if (typeof item === "object") {
|
|
369
|
+
if (!(0, import_utils.isValidFilter)(item.filter)) {
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
const result = await UserRepo.find({
|
|
373
|
+
...item,
|
|
374
|
+
fields: ["id"],
|
|
375
|
+
transaction: processor.mainTransaction
|
|
376
|
+
});
|
|
377
|
+
result.forEach((user) => addAssignee(user.id));
|
|
378
|
+
} else {
|
|
379
|
+
if (validIdSet.has(item)) {
|
|
380
|
+
addAssignee(item);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
return assignees;
|
|
385
|
+
}
|
|
386
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
387
|
+
0 && (module.exports = {
|
|
388
|
+
AIEmployeeInstruction,
|
|
389
|
+
...require("./handler"),
|
|
390
|
+
...require("./tools")
|
|
391
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
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
|
+
import { DynamicToolsProvider } from '@nocobase/ai';
|
|
10
|
+
import { Plugin } from '@nocobase/server';
|
|
11
|
+
export type WorkflowTaskToolProvider = (plugin: Plugin) => DynamicToolsProvider;
|
|
12
|
+
export declare const getWorkflowTasks: WorkflowTaskToolProvider;
|
|
@@ -0,0 +1,133 @@
|
|
|
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
|
+
var __defProp = Object.defineProperty;
|
|
11
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
12
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
13
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
14
|
+
var __export = (target, all) => {
|
|
15
|
+
for (var name in all)
|
|
16
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
17
|
+
};
|
|
18
|
+
var __copyProps = (to, from, except, desc) => {
|
|
19
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
20
|
+
for (let key of __getOwnPropNames(from))
|
|
21
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
22
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
23
|
+
}
|
|
24
|
+
return to;
|
|
25
|
+
};
|
|
26
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
27
|
+
var tools_exports = {};
|
|
28
|
+
__export(tools_exports, {
|
|
29
|
+
getWorkflowTasks: () => getWorkflowTasks
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(tools_exports);
|
|
32
|
+
var import_plugin_workflow = require("@nocobase/plugin-workflow");
|
|
33
|
+
const getWorkflowTasks = (plugin) => async (register, filter) => {
|
|
34
|
+
if (!(filter == null ? void 0 : filter.sessionId)) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const workflowPlugin = plugin.app.pm.get("workflow");
|
|
38
|
+
const task = await plugin.db.getRepository("aiWorkflowTasks").findOne({
|
|
39
|
+
filter: {
|
|
40
|
+
sessionId: filter == null ? void 0 : filter.sessionId
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
if (!task) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const execution = await plugin.db.getRepository("executions").findByTargetKey(task.executionId);
|
|
47
|
+
if (!execution) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const flowNode = await plugin.db.getRepository("flow_nodes").findByTargetKey(task.nodeId);
|
|
51
|
+
if (!flowNode) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const processor = workflowPlugin.createProcessor(execution);
|
|
55
|
+
const config = processor.getParsedValue(flowNode.config, flowNode.id);
|
|
56
|
+
const outputSchema = config.structuredOutput.schema;
|
|
57
|
+
const parsedOutputSchema = typeof outputSchema === "string" ? JSON.parse(outputSchema) : outputSchema;
|
|
58
|
+
const resultSchema = parsedOutputSchema && typeof parsedOutputSchema === "object" && !Array.isArray(parsedOutputSchema) ? {
|
|
59
|
+
...parsedOutputSchema,
|
|
60
|
+
description: parsedOutputSchema.description || `Return the final structured output for this workflow task in this field.
|
|
61
|
+
The value must match the required schema exactly so the workflow can continue.
|
|
62
|
+
Do not include free-form text, extra fields, or any structure outside the defined schema.`
|
|
63
|
+
} : parsedOutputSchema;
|
|
64
|
+
const schema = {
|
|
65
|
+
type: "object",
|
|
66
|
+
properties: {
|
|
67
|
+
result: resultSchema
|
|
68
|
+
},
|
|
69
|
+
additionalProperties: false
|
|
70
|
+
};
|
|
71
|
+
if (config.requiresApproval === "ai_decision") {
|
|
72
|
+
schema.properties.requiresApproval = {
|
|
73
|
+
type: "boolean",
|
|
74
|
+
description: `This field is mandatory.
|
|
75
|
+
Set it to true whenever the human user still needs to review the result, confirm it, make a decision,
|
|
76
|
+
provide missing information, answer your follow-up questions, or correct the result.
|
|
77
|
+
Set it to false only when you have determined that the task is fully complete, the result you are returning fully satisfies the required schema and the task requirements,
|
|
78
|
+
and no human user needs to review the result or provide any additional information before the workflow continues.
|
|
79
|
+
Warning: if requiresApproval=false, this AI workflow node ends immediately, the workflow moves to the next node,
|
|
80
|
+
and the human will no longer be able to continue this conversation with the AI.
|
|
81
|
+
If a human may still need to say anything else, do not set it to false.`
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
register.registerTools({
|
|
85
|
+
scope: "SPECIFIED",
|
|
86
|
+
defaultPermission: config.requiresApproval !== "no_required" ? "ASK" : "ALLOW",
|
|
87
|
+
from: "workflow",
|
|
88
|
+
definition: {
|
|
89
|
+
name: "aiEmployeeWorkflowTaskOutput",
|
|
90
|
+
description: `Use this tool to return the structured output required by the workflow so execution can continue.
|
|
91
|
+
Every time you call it, you must provide the final data strictly under result and match the result schema exactly.
|
|
92
|
+
Do not return free-form text, extra fields, or any structure outside the defined result.`,
|
|
93
|
+
schema
|
|
94
|
+
},
|
|
95
|
+
invoke: async (_ctx, args) => {
|
|
96
|
+
const job = await plugin.db.getModel("jobs").findByPk(task.jobId);
|
|
97
|
+
if (!job) {
|
|
98
|
+
return {
|
|
99
|
+
status: "fail",
|
|
100
|
+
message: "job not existed"
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
if (job.status === import_plugin_workflow.JOB_STATUS.ABORTED) {
|
|
104
|
+
return {
|
|
105
|
+
status: "success"
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
await plugin.db.getRepository("aiWorkflowTasks").update({
|
|
109
|
+
values: {
|
|
110
|
+
status: "approved"
|
|
111
|
+
},
|
|
112
|
+
filter: {
|
|
113
|
+
id: task.id,
|
|
114
|
+
status: {
|
|
115
|
+
$ne: "aborted"
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
job.set({
|
|
120
|
+
status: import_plugin_workflow.JOB_STATUS.RESOLVED,
|
|
121
|
+
result: args.result
|
|
122
|
+
});
|
|
123
|
+
await workflowPlugin.resume(job);
|
|
124
|
+
return {
|
|
125
|
+
status: "success"
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
};
|
|
130
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
131
|
+
0 && (module.exports = {
|
|
132
|
+
getWorkflowTasks
|
|
133
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
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
|
+
export type AIEmployeeInstructionConfig = {
|
|
10
|
+
username: string;
|
|
11
|
+
message: {
|
|
12
|
+
user: string;
|
|
13
|
+
system?: string;
|
|
14
|
+
};
|
|
15
|
+
skillSettings?: {
|
|
16
|
+
skills?: string[];
|
|
17
|
+
tools?: {
|
|
18
|
+
name: string;
|
|
19
|
+
}[];
|
|
20
|
+
};
|
|
21
|
+
webSearch?: boolean;
|
|
22
|
+
model: {
|
|
23
|
+
llmService: string;
|
|
24
|
+
model: string;
|
|
25
|
+
};
|
|
26
|
+
requiresApproval?: 'no_required' | 'ai_decision' | 'human_decision';
|
|
27
|
+
assignees?: string[];
|
|
28
|
+
userId: string;
|
|
29
|
+
files: AIEmployeeInstructionFiles[];
|
|
30
|
+
};
|
|
31
|
+
export type AIEmployeeInstructionFiles = {
|
|
32
|
+
type: 'file_id' | 'file_url';
|
|
33
|
+
collection?: string;
|
|
34
|
+
value: string;
|
|
35
|
+
};
|