@nocobase/plugin-ai 2.4.0-alpha.3 → 2.4.0-alpha.4
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/client/{244.276308c9b2090ec2.js → 244.12a92ac906b07849.js} +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client-v2/{244.e85b8d4659ca66c0.js → 244.a0c8684f23fa5fa9.js} +1 -1
- package/dist/client-v2/index.js +1 -1
- package/dist/common/ai-employee-validation.d.ts +2 -0
- package/dist/common/ai-employee-validation.js +6 -0
- package/dist/common/error-codes.d.ts +1 -0
- package/dist/common/error-codes.js +3 -0
- package/dist/externalVersion.js +15 -15
- package/dist/locale/en-US.json +2 -0
- package/dist/locale/zh-CN.json +2 -0
- package/dist/node_modules/@langchain/mistralai/package.json +1 -1
- package/dist/node_modules/@langchain/xai/package.json +1 -1
- package/dist/node_modules/fs-extra/package.json +1 -1
- package/dist/node_modules/jsonrepair/package.json +1 -1
- package/dist/node_modules/just-bash/package.json +1 -1
- package/dist/node_modules/nodejs-snowflake/package.json +1 -1
- package/dist/node_modules/openai/package.json +1 -1
- package/dist/node_modules/zod/package.json +1 -1
- package/dist/server/ai-employees/ai-employee.d.ts +28 -1
- package/dist/server/ai-employees/ai-employee.js +257 -34
- package/dist/server/ai-employees/ai-knowledge-base.js +11 -4
- package/dist/server/ai-employees/middleware/conversation.js +19 -20
- package/dist/server/ai-employees/middleware/index.d.ts +1 -0
- package/dist/server/ai-employees/middleware/index.js +2 -0
- package/dist/server/ai-employees/middleware/tool-result-integrity.d.ts +22 -0
- package/dist/server/ai-employees/middleware/tool-result-integrity.js +211 -0
- package/dist/server/resource/aiEmployees.js +26 -0
- package/package.json +2 -2
|
@@ -0,0 +1,211 @@
|
|
|
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 tool_result_integrity_exports = {};
|
|
28
|
+
__export(tool_result_integrity_exports, {
|
|
29
|
+
isToolCallHistoryValid: () => isToolCallHistoryValid,
|
|
30
|
+
normalizeToolCallHistory: () => normalizeToolCallHistory,
|
|
31
|
+
toolResultIntegrityMiddleware: () => toolResultIntegrityMiddleware
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(tool_result_integrity_exports);
|
|
34
|
+
var import_messages = require("@langchain/core/messages");
|
|
35
|
+
var import_langchain = require("langchain");
|
|
36
|
+
const SYNTHETIC_TOOL_RESULT_ERROR = "Tool execution was interrupted before a result was recorded.";
|
|
37
|
+
const getValidToolCalls = (message) => (message.tool_calls ?? []).filter((toolCall) => typeof toolCall.id === "string" && toolCall.id.length > 0);
|
|
38
|
+
const isToolCallHistoryValid = (messages) => {
|
|
39
|
+
for (let index = 0; index < messages.length; index++) {
|
|
40
|
+
const message = messages[index];
|
|
41
|
+
if (import_messages.ToolMessage.isInstance(message)) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
if (!import_messages.AIMessage.isInstance(message)) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
const toolCalls = getValidToolCalls(message);
|
|
48
|
+
if (!toolCalls.length) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
const expectedToolCallIds = new Set(toolCalls.map((toolCall) => toolCall.id));
|
|
52
|
+
const seenToolResultIds = /* @__PURE__ */ new Set();
|
|
53
|
+
for (let resultIndex = 0; resultIndex < toolCalls.length; resultIndex++) {
|
|
54
|
+
index++;
|
|
55
|
+
if (index >= messages.length) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
const result = messages[index];
|
|
59
|
+
if (!import_messages.ToolMessage.isInstance(result) || !expectedToolCallIds.has(result.tool_call_id) || seenToolResultIds.has(result.tool_call_id)) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
seenToolResultIds.add(result.tool_call_id);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return true;
|
|
66
|
+
};
|
|
67
|
+
const serializeToolResultContent = (content) => {
|
|
68
|
+
if (typeof content === "string") {
|
|
69
|
+
return content;
|
|
70
|
+
}
|
|
71
|
+
return JSON.stringify(content ?? null);
|
|
72
|
+
};
|
|
73
|
+
const createSyntheticToolResult = (sessionId, toolCall) => new import_messages.ToolMessage({
|
|
74
|
+
id: `synthetic-tool-result:${sessionId}:${toolCall.id}`,
|
|
75
|
+
tool_call_id: toolCall.id,
|
|
76
|
+
name: toolCall.name,
|
|
77
|
+
status: "error",
|
|
78
|
+
content: JSON.stringify({
|
|
79
|
+
status: "error",
|
|
80
|
+
error: SYNTHETIC_TOOL_RESULT_ERROR
|
|
81
|
+
})
|
|
82
|
+
});
|
|
83
|
+
const createRestoredToolResult = (sessionId, toolCall, persisted) => {
|
|
84
|
+
if (!persisted || persisted.invokeStatus !== "confirmed" && persisted.invokeStatus !== "done") {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
return new import_messages.ToolMessage({
|
|
89
|
+
id: `restored-tool-result:${sessionId}:${toolCall.id}`,
|
|
90
|
+
tool_call_id: toolCall.id,
|
|
91
|
+
name: toolCall.name,
|
|
92
|
+
status: persisted.status === "error" ? "error" : "success",
|
|
93
|
+
content: serializeToolResultContent(persisted.content)
|
|
94
|
+
});
|
|
95
|
+
} catch {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
const countMisplacedResults = (messages) => {
|
|
100
|
+
let misplacedResultCount = 0;
|
|
101
|
+
for (let index = 0; index < messages.length; index++) {
|
|
102
|
+
const message = messages[index];
|
|
103
|
+
if (!import_messages.AIMessage.isInstance(message)) {
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
const toolCalls = getValidToolCalls(message);
|
|
107
|
+
if (!toolCalls.length) {
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const expectedToolCallIds = new Set(toolCalls.map((toolCall) => toolCall.id));
|
|
111
|
+
const contiguousResultIds = /* @__PURE__ */ new Set();
|
|
112
|
+
for (let offset = 1; offset <= toolCalls.length; offset++) {
|
|
113
|
+
const result = messages[index + offset];
|
|
114
|
+
if (!import_messages.ToolMessage.isInstance(result)) {
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
if (expectedToolCallIds.has(result.tool_call_id)) {
|
|
118
|
+
contiguousResultIds.add(result.tool_call_id);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
misplacedResultCount += expectedToolCallIds.size - contiguousResultIds.size;
|
|
122
|
+
}
|
|
123
|
+
return misplacedResultCount;
|
|
124
|
+
};
|
|
125
|
+
const normalizeToolCallHistory = async (messages, options) => {
|
|
126
|
+
var _a;
|
|
127
|
+
const toolResultsByCallId = /* @__PURE__ */ new Map();
|
|
128
|
+
const referencedToolCallIds = /* @__PURE__ */ new Set();
|
|
129
|
+
let toolCallCount = 0;
|
|
130
|
+
let toolResultCount = 0;
|
|
131
|
+
for (const message of messages) {
|
|
132
|
+
if (import_messages.AIMessage.isInstance(message)) {
|
|
133
|
+
for (const toolCall of getValidToolCalls(message)) {
|
|
134
|
+
toolCallCount++;
|
|
135
|
+
referencedToolCallIds.add(toolCall.id);
|
|
136
|
+
}
|
|
137
|
+
} else if (import_messages.ToolMessage.isInstance(message)) {
|
|
138
|
+
toolResultCount++;
|
|
139
|
+
if (message.tool_call_id) {
|
|
140
|
+
const results = toolResultsByCallId.get(message.tool_call_id) ?? [];
|
|
141
|
+
results.push(message);
|
|
142
|
+
toolResultsByCallId.set(message.tool_call_id, results);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
const missingToolCallIds = Array.from(referencedToolCallIds).filter((id) => !toolResultsByCallId.has(id));
|
|
147
|
+
const persistedResults = missingToolCallIds.length ? await options.loadToolResults(missingToolCallIds) : /* @__PURE__ */ new Map();
|
|
148
|
+
const normalized = [];
|
|
149
|
+
const nextToolResultIndexByCallId = /* @__PURE__ */ new Map();
|
|
150
|
+
let restoredResultCount = 0;
|
|
151
|
+
let syntheticResultCount = 0;
|
|
152
|
+
let consumedExistingResultCount = 0;
|
|
153
|
+
for (const message of messages) {
|
|
154
|
+
if (import_messages.ToolMessage.isInstance(message)) {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
normalized.push(message);
|
|
158
|
+
if (!import_messages.AIMessage.isInstance(message)) {
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
for (const toolCall of getValidToolCalls(message)) {
|
|
162
|
+
const existingResults = toolResultsByCallId.get(toolCall.id);
|
|
163
|
+
const existingResultIndex = nextToolResultIndexByCallId.get(toolCall.id) ?? 0;
|
|
164
|
+
const existingResult = existingResults == null ? void 0 : existingResults[existingResultIndex];
|
|
165
|
+
if (existingResult) {
|
|
166
|
+
nextToolResultIndexByCallId.set(toolCall.id, existingResultIndex + 1);
|
|
167
|
+
normalized.push(existingResult);
|
|
168
|
+
consumedExistingResultCount++;
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
const restoredResult = createRestoredToolResult(options.sessionId, toolCall, persistedResults.get(toolCall.id));
|
|
172
|
+
if (restoredResult) {
|
|
173
|
+
normalized.push(restoredResult);
|
|
174
|
+
restoredResultCount++;
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
normalized.push(createSyntheticToolResult(options.sessionId, toolCall));
|
|
178
|
+
syntheticResultCount++;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
const stats = {
|
|
182
|
+
messageCount: messages.length,
|
|
183
|
+
toolCallCount,
|
|
184
|
+
missingResultCount: missingToolCallIds.length,
|
|
185
|
+
misplacedResultCount: countMisplacedResults(messages),
|
|
186
|
+
orphanResultCount: toolResultCount - consumedExistingResultCount,
|
|
187
|
+
restoredResultCount,
|
|
188
|
+
syntheticResultCount
|
|
189
|
+
};
|
|
190
|
+
(_a = options.logger) == null ? void 0 : _a.warn("Normalize invalid tool call history before model call", {
|
|
191
|
+
sessionId: options.sessionId,
|
|
192
|
+
...stats
|
|
193
|
+
});
|
|
194
|
+
return normalized;
|
|
195
|
+
};
|
|
196
|
+
const toolResultIntegrityMiddleware = (options) => (0, import_langchain.createMiddleware)({
|
|
197
|
+
name: "ToolResultIntegrityMiddleware",
|
|
198
|
+
wrapModelCall: async (request, handler) => {
|
|
199
|
+
if (isToolCallHistoryValid(request.messages)) {
|
|
200
|
+
return handler(request);
|
|
201
|
+
}
|
|
202
|
+
request.messages = await normalizeToolCallHistory(request.messages, options);
|
|
203
|
+
return handler(request);
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
207
|
+
0 && (module.exports = {
|
|
208
|
+
isToolCallHistoryValid,
|
|
209
|
+
normalizeToolCallHistory,
|
|
210
|
+
toolResultIntegrityMiddleware
|
|
211
|
+
});
|
|
@@ -88,10 +88,30 @@ const setDefaultKnowledgeBaseRetrievalStrategy = (ctx) => {
|
|
|
88
88
|
values.knowledgeBase = (0, import_ai_knowledge_base.withDefaultKnowledgeBaseRetrievalStrategy)(values.knowledgeBase);
|
|
89
89
|
}
|
|
90
90
|
};
|
|
91
|
+
const readEmployeeValue = (employee, key) => typeof (employee == null ? void 0 : employee.get) === "function" ? employee.get(key) : employee == null ? void 0 : employee[key];
|
|
92
|
+
const validateKnowledgeBasePrompt = (ctx, employee) => {
|
|
93
|
+
const values = ctx.action.params.values;
|
|
94
|
+
if (!isRecord(values)) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const enableKnowledgeBase = typeof values.enableKnowledgeBase === "boolean" ? values.enableKnowledgeBase : readEmployeeValue(employee, "enableKnowledgeBase") === true;
|
|
98
|
+
const knowledgeBasePrompt = "knowledgeBasePrompt" in values ? values.knowledgeBasePrompt : readEmployeeValue(employee, "knowledgeBasePrompt");
|
|
99
|
+
if (enableKnowledgeBase && !(0, import_ai_employee_validation.hasKnowledgeBaseDataPlaceholder)(knowledgeBasePrompt)) {
|
|
100
|
+
ctx.throw(400, {
|
|
101
|
+
code: import_error_codes.AI_EMPLOYEE_KNOWLEDGE_BASE_PROMPT_INVALID,
|
|
102
|
+
message: ctx.t("The Knowledge Base Prompt must include {knowledgeBaseData} before you can save it."),
|
|
103
|
+
data: { field: "knowledgeBasePrompt" }
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
const hasKnowledgeBaseConfigurationChanges = (values) => isRecord(values) && ["enableKnowledgeBase", "knowledgeBase", "knowledgeBasePrompt"].some(
|
|
108
|
+
(key) => Object.prototype.hasOwnProperty.call(values, key)
|
|
109
|
+
);
|
|
91
110
|
const create = async (ctx, next) => {
|
|
92
111
|
var _a;
|
|
93
112
|
validateAndNormalizeProfileValues(ctx);
|
|
94
113
|
setDefaultKnowledgeBaseRetrievalStrategy(ctx);
|
|
114
|
+
validateKnowledgeBasePrompt(ctx);
|
|
95
115
|
const username = (_a = ctx.action.params.values) == null ? void 0 : _a.username;
|
|
96
116
|
if (typeof username === "string") {
|
|
97
117
|
const existingEmployee = await ctx.db.getRepository("aiEmployees").findOne({
|
|
@@ -112,6 +132,12 @@ const create = async (ctx, next) => {
|
|
|
112
132
|
};
|
|
113
133
|
const update = async (ctx, next) => {
|
|
114
134
|
validateAndNormalizeProfileValues(ctx);
|
|
135
|
+
if (hasKnowledgeBaseConfigurationChanges(ctx.action.params.values)) {
|
|
136
|
+
const employee = await ctx.db.getRepository("aiEmployees").findOne({
|
|
137
|
+
filterByTk: ctx.action.params.filterByTk
|
|
138
|
+
});
|
|
139
|
+
validateKnowledgeBasePrompt(ctx, employee);
|
|
140
|
+
}
|
|
115
141
|
await import_actions.default.update(ctx, next);
|
|
116
142
|
};
|
|
117
143
|
const list = async (ctx, next) => {
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"description": "Create AI employees with diverse skills to collaborate with humans, build systems, and handle business operations.",
|
|
7
7
|
"description.ru-RU": "Поддержка интеграции с AI-сервисами: предоставляются AI-узлы для рабочих процессов, расширяя возможности бизнес-обработки.",
|
|
8
8
|
"description.zh-CN": "创建各种技能的 AI 员工,与人类协同,搭建系统,处理业务。",
|
|
9
|
-
"version": "2.4.0-alpha.
|
|
9
|
+
"version": "2.4.0-alpha.4",
|
|
10
10
|
"main": "dist/server/index.js",
|
|
11
11
|
"homepage": "https://docs.nocobase.com/handbook/action-ai",
|
|
12
12
|
"homepage.ru-RU": "https://docs-ru.nocobase.com/handbook/action-ai",
|
|
@@ -66,5 +66,5 @@
|
|
|
66
66
|
"keywords": [
|
|
67
67
|
"AI"
|
|
68
68
|
],
|
|
69
|
-
"gitHead": "
|
|
69
|
+
"gitHead": "527910882037ce1558ee6b0ef748208b8d6996bb"
|
|
70
70
|
}
|