@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.
Files changed (29) hide show
  1. package/dist/client/{244.276308c9b2090ec2.js → 244.12a92ac906b07849.js} +1 -1
  2. package/dist/client/index.js +1 -1
  3. package/dist/client-v2/{244.e85b8d4659ca66c0.js → 244.a0c8684f23fa5fa9.js} +1 -1
  4. package/dist/client-v2/index.js +1 -1
  5. package/dist/common/ai-employee-validation.d.ts +2 -0
  6. package/dist/common/ai-employee-validation.js +6 -0
  7. package/dist/common/error-codes.d.ts +1 -0
  8. package/dist/common/error-codes.js +3 -0
  9. package/dist/externalVersion.js +15 -15
  10. package/dist/locale/en-US.json +2 -0
  11. package/dist/locale/zh-CN.json +2 -0
  12. package/dist/node_modules/@langchain/mistralai/package.json +1 -1
  13. package/dist/node_modules/@langchain/xai/package.json +1 -1
  14. package/dist/node_modules/fs-extra/package.json +1 -1
  15. package/dist/node_modules/jsonrepair/package.json +1 -1
  16. package/dist/node_modules/just-bash/package.json +1 -1
  17. package/dist/node_modules/nodejs-snowflake/package.json +1 -1
  18. package/dist/node_modules/openai/package.json +1 -1
  19. package/dist/node_modules/zod/package.json +1 -1
  20. package/dist/server/ai-employees/ai-employee.d.ts +28 -1
  21. package/dist/server/ai-employees/ai-employee.js +257 -34
  22. package/dist/server/ai-employees/ai-knowledge-base.js +11 -4
  23. package/dist/server/ai-employees/middleware/conversation.js +19 -20
  24. package/dist/server/ai-employees/middleware/index.d.ts +1 -0
  25. package/dist/server/ai-employees/middleware/index.js +2 -0
  26. package/dist/server/ai-employees/middleware/tool-result-integrity.d.ts +22 -0
  27. package/dist/server/ai-employees/middleware/tool-result-integrity.js +211 -0
  28. package/dist/server/resource/aiEmployees.js +26 -0
  29. package/package.json +2 -2
@@ -60,6 +60,7 @@ var import_attachments = require("../attachments");
60
60
  var import_frontend_tools = require("../../common/frontend-tools");
61
61
  var import_frontend_tools2 = require("../frontend-tools");
62
62
  var import_reasoning_stream_state = require("./reasoning-stream-state");
63
+ const ABORTED_TOOL_CALL_CONTENT = "The tool call was interrupted because the conversation was aborted.";
63
64
  class AIEmployee {
64
65
  sessionId;
65
66
  from = "main-agent";
@@ -78,6 +79,7 @@ class AIEmployee {
78
79
  tools;
79
80
  inWorkflow;
80
81
  streamCached;
82
+ static conversationPersistenceQueues = /* @__PURE__ */ new WeakMap();
81
83
  constructor({
82
84
  ctx,
83
85
  employee,
@@ -419,6 +421,7 @@ class AIEmployee {
419
421
  async processChatStream(stream, options) {
420
422
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z;
421
423
  const aiMessageIdMap = /* @__PURE__ */ new Map();
424
+ const persistedAIMessageIdMap = /* @__PURE__ */ new Map();
422
425
  const { signal, providerName, llmService, model, provider, responseMetadata, allowEmpty = false } = options;
423
426
  const reasoningState = new import_reasoning_stream_state.ReasoningStreamState();
424
427
  const stopReasoning = async (conversation) => {
@@ -432,37 +435,38 @@ class AIEmployee {
432
435
  }
433
436
  };
434
437
  let gathered;
435
- signal.addEventListener("abort", async () => {
436
- try {
437
- await stopAllReasoning();
438
- if ((gathered == null ? void 0 : gathered.type) === "ai") {
439
- const values = (0, import_utils2.convertAIMessage)({
440
- aiEmployee: this,
441
- providerName,
442
- provider,
443
- llmService,
444
- model,
445
- aiMessage: gathered
446
- });
447
- if (values) {
448
- values.metadata.interrupted = true;
449
- }
450
- await this.aiChatConversation.withTransaction(async (conversation, transaction) => {
451
- const result = await conversation.addMessages(values);
452
- });
453
- }
454
- } catch (e) {
455
- this.logger.error("Fail to save message after conversation abort", gathered);
456
- } finally {
457
- await this.aiConversationsRepo.update({
458
- values: { llmActiveState: "idle", read: true },
459
- filter: {
460
- sessionId: this.sessionId
438
+ let abortFinalization;
439
+ signal.addEventListener(
440
+ "abort",
441
+ () => {
442
+ abortFinalization = (async () => {
443
+ try {
444
+ await stopAllReasoning();
445
+ if ((gathered == null ? void 0 : gathered.type) === "ai") {
446
+ await this.finalizeAbortedAIMessage({
447
+ aiMessage: gathered,
448
+ providerName,
449
+ provider,
450
+ llmService,
451
+ model,
452
+ knownMessageId: persistedAIMessageIdMap.get(gathered.id)
453
+ });
454
+ }
455
+ } catch (e) {
456
+ this.logger.error("Fail to save message after conversation abort", gathered);
457
+ } finally {
458
+ await this.aiConversationsRepo.update({
459
+ values: { llmActiveState: "idle", read: true },
460
+ filter: {
461
+ sessionId: this.sessionId
462
+ }
463
+ });
464
+ await this.streamCached.clear();
461
465
  }
462
- });
463
- await this.streamCached.clear();
464
- }
465
- });
466
+ })();
467
+ },
468
+ { once: true }
469
+ );
466
470
  try {
467
471
  const aiEmployeeConversation = {
468
472
  sessionId: this.sessionId,
@@ -525,6 +529,7 @@ class AIEmployee {
525
529
  if (chunks.action === "AfterAIMessageSaved") {
526
530
  await this.streamCached.skipped();
527
531
  aiMessageIdMap.set(currentConversation.sessionId, chunks.body.messageId);
532
+ persistedAIMessageIdMap.set(chunks.body.id, chunks.body.messageId);
528
533
  const data = responseMetadata.get(chunks.body.id);
529
534
  if (data) {
530
535
  const savedMessage = await this.aiMessagesModel.findOne({
@@ -629,6 +634,7 @@ class AIEmployee {
629
634
  this.sendErrorResponse(provider.parseResponseError(err));
630
635
  }
631
636
  } finally {
637
+ await abortFinalization;
632
638
  if (this.from === "main-agent") {
633
639
  this.ctx.res.end();
634
640
  }
@@ -804,6 +810,170 @@ If information is missing, clearly state it in the summary.</Important>`;
804
810
  }
805
811
  }
806
812
  // === Tool calls ===
813
+ async withLockedConversation(callback) {
814
+ const persistenceQueues = AIEmployee.conversationPersistenceQueues.get(this.db) ?? /* @__PURE__ */ new Map();
815
+ AIEmployee.conversationPersistenceQueues.set(this.db, persistenceQueues);
816
+ const previous = persistenceQueues.get(this.sessionId) ?? Promise.resolve();
817
+ let release;
818
+ const current = new Promise((resolve) => {
819
+ release = resolve;
820
+ });
821
+ persistenceQueues.set(this.sessionId, current);
822
+ await previous;
823
+ try {
824
+ for (let attempt = 0; attempt < 2; attempt++) {
825
+ try {
826
+ return await this.aiChatConversation.withTransaction(async (conversation, transaction) => {
827
+ const lockedConversation = await this.aiConversationsModel.findOne({
828
+ where: { sessionId: this.sessionId },
829
+ transaction,
830
+ lock: transaction.LOCK.UPDATE
831
+ });
832
+ if (!lockedConversation) {
833
+ throw new Error(`AI conversation ${this.sessionId} not found`);
834
+ }
835
+ return await callback(conversation, transaction);
836
+ });
837
+ } catch (error) {
838
+ if (!(error instanceof import_database.UniqueConstraintError) || attempt === 1) {
839
+ throw error;
840
+ }
841
+ }
842
+ }
843
+ throw new Error(`Failed to persist AI conversation ${this.sessionId}`);
844
+ } finally {
845
+ release();
846
+ if (persistenceQueues.get(this.sessionId) === current) {
847
+ persistenceQueues.delete(this.sessionId);
848
+ }
849
+ }
850
+ }
851
+ async findPersistedAIMessage(transaction, langChainMessageId, knownMessageId) {
852
+ var _a;
853
+ if (knownMessageId) {
854
+ const knownMessage = await this.aiMessagesModel.findOne({
855
+ where: { sessionId: this.sessionId, messageId: knownMessageId },
856
+ transaction
857
+ });
858
+ if (((_a = knownMessage == null ? void 0 : knownMessage.get("metadata")) == null ? void 0 : _a.id) === langChainMessageId) {
859
+ return knownMessage;
860
+ }
861
+ }
862
+ const messages = await this.aiMessagesModel.findAll({
863
+ where: { sessionId: this.sessionId, role: this.employee.username },
864
+ order: [["messageId", "DESC"]],
865
+ transaction
866
+ });
867
+ return messages.find((message) => {
868
+ var _a2;
869
+ return ((_a2 = message.get("metadata")) == null ? void 0 : _a2.id) === langChainMessageId;
870
+ });
871
+ }
872
+ async persistAIMessageInTransaction(conversation, transaction, options) {
873
+ const existingMessage = await this.findPersistedAIMessage(
874
+ transaction,
875
+ options.langChainMessageId,
876
+ options.knownMessageId
877
+ );
878
+ if (existingMessage) {
879
+ const message2 = existingMessage.toJSON();
880
+ const initializedToolCalls2 = await this.aiToolMessagesModel.findAll({
881
+ where: { sessionId: this.sessionId, messageId: message2.messageId },
882
+ transaction
883
+ });
884
+ return { message: message2, initializedToolCalls: initializedToolCalls2, created: false };
885
+ }
886
+ const message = await conversation.addMessages(options.values);
887
+ const initializedToolCalls = options.toolCalls.length ? await this.initToolCall(transaction, message.messageId, options.toolCalls) : [];
888
+ return { message, initializedToolCalls, created: true };
889
+ }
890
+ async persistAIMessage(options) {
891
+ return await this.withLockedConversation(async (conversation, transaction) => {
892
+ return await this.persistAIMessageInTransaction(conversation, transaction, options);
893
+ });
894
+ }
895
+ async finalizeAbortedAIMessage({
896
+ aiMessage,
897
+ providerName,
898
+ provider,
899
+ llmService,
900
+ model,
901
+ knownMessageId
902
+ }) {
903
+ const values = (0, import_utils2.convertAIMessage)({
904
+ aiEmployee: this,
905
+ providerName,
906
+ provider,
907
+ llmService,
908
+ model,
909
+ aiMessage
910
+ });
911
+ if (!values) {
912
+ return;
913
+ }
914
+ values.metadata = { ...values.metadata, interrupted: true };
915
+ const toolCalls = aiMessage.tool_calls ?? [];
916
+ return await this.withLockedConversation(async (conversation, transaction) => {
917
+ var _a;
918
+ const result = await this.persistAIMessageInTransaction(conversation, transaction, {
919
+ values,
920
+ langChainMessageId: aiMessage.id,
921
+ toolCalls,
922
+ knownMessageId
923
+ });
924
+ const unfinishedToolCalls = result.initializedToolCalls.map((toolCall) => toolCall.toJSON()).filter((toolCall) => toolCall.invokeStatus !== "confirmed");
925
+ if (!result.created && (!result.initializedToolCalls.length || unfinishedToolCalls.length) && !((_a = result.message.metadata) == null ? void 0 : _a.interrupted)) {
926
+ const metadata = { ...result.message.metadata, interrupted: true };
927
+ await this.aiMessagesModel.update(
928
+ { metadata },
929
+ { where: { sessionId: this.sessionId, messageId: result.message.messageId }, transaction }
930
+ );
931
+ result.message.metadata = metadata;
932
+ }
933
+ if (!unfinishedToolCalls.length) {
934
+ return result;
935
+ }
936
+ const persistedToolCalls = result.message.toolCalls ?? toolCalls;
937
+ const toolCallMap = new Map(persistedToolCalls.map((toolCall) => [toolCall.id, toolCall]));
938
+ const now = /* @__PURE__ */ new Date();
939
+ await conversation.addMessages(
940
+ unfinishedToolCalls.map((toolCall) => ({
941
+ role: "tool",
942
+ content: { type: "text", content: ABORTED_TOOL_CALL_CONTENT },
943
+ metadata: {
944
+ id: `aborted-tool:${aiMessage.id}:${toolCall.toolCallId}`,
945
+ model,
946
+ provider: providerName,
947
+ llmService,
948
+ messageId: result.message.messageId,
949
+ toolCallId: toolCall.toolCallId,
950
+ toolName: toolCall.toolName,
951
+ toolCall: toolCallMap.get(toolCall.toolCallId),
952
+ autoCall: toolCall.auto
953
+ }
954
+ }))
955
+ );
956
+ for (const toolCall of unfinishedToolCalls) {
957
+ await this.aiToolMessagesModel.update(
958
+ {
959
+ invokeStatus: "confirmed",
960
+ status: "error",
961
+ content: ABORTED_TOOL_CALL_CONTENT,
962
+ invokeStartTime: toolCall.invokeStartTime ?? now,
963
+ invokeEndTime: now
964
+ },
965
+ {
966
+ where: {
967
+ id: toolCall.id,
968
+ invokeStatus: { [import_database.Op.ne]: "confirmed" }
969
+ },
970
+ transaction
971
+ }
972
+ );
973
+ }
974
+ return result;
975
+ });
976
+ }
807
977
  async initToolCall(transaction, messageId, toolCalls) {
808
978
  const nowTime = /* @__PURE__ */ new Date();
809
979
  const toolMap = await this.getToolsMap();
@@ -956,6 +1126,39 @@ If information is missing, clearly state it in the summary.</Important>`;
956
1126
  })).map((it) => it.toJSON());
957
1127
  return new Map(list.map((it) => [it.toolCallId, it]));
958
1128
  }
1129
+ async getToolCallResults(toolCallIds) {
1130
+ if (!toolCallIds.length) {
1131
+ return /* @__PURE__ */ new Map();
1132
+ }
1133
+ const list = (await this.aiToolMessagesModel.findAll({
1134
+ where: {
1135
+ sessionId: this.sessionId,
1136
+ toolCallId: {
1137
+ [import_database.Op.in]: toolCallIds
1138
+ }
1139
+ }
1140
+ })).map((item) => item.toJSON());
1141
+ const invokeStatusPriority = { confirmed: 2, done: 1 };
1142
+ const results = /* @__PURE__ */ new Map();
1143
+ for (const item of list) {
1144
+ if (item.invokeStatus !== "confirmed" && item.invokeStatus !== "done") {
1145
+ continue;
1146
+ }
1147
+ const existing = results.get(item.toolCallId);
1148
+ if (!existing) {
1149
+ results.set(item.toolCallId, item);
1150
+ continue;
1151
+ }
1152
+ const priority = invokeStatusPriority[item.invokeStatus];
1153
+ const existingPriority = invokeStatusPriority[existing.invokeStatus];
1154
+ const updatedAt = item.updatedAt ? new Date(item.updatedAt).getTime() : 0;
1155
+ const existingUpdatedAt = existing.updatedAt ? new Date(existing.updatedAt).getTime() : 0;
1156
+ if (priority > existingPriority || priority === existingPriority && updatedAt > existingUpdatedAt) {
1157
+ results.set(item.toolCallId, item);
1158
+ }
1159
+ }
1160
+ return results;
1161
+ }
959
1162
  async cancelToolCall() {
960
1163
  var _a;
961
1164
  let messageId;
@@ -1093,7 +1296,7 @@ If information is missing, clearly state it in the summary.</Important>`;
1093
1296
  });
1094
1297
  }
1095
1298
  async formatMessages({ messages, provider }) {
1096
- var _a, _b;
1299
+ var _a, _b, _c, _d;
1097
1300
  const formattedMessages = [];
1098
1301
  const workContextHandler = this.plugin.workContextHandler;
1099
1302
  const normalizedMessages = await this.normalizeMessageAttachments(messages);
@@ -1160,13 +1363,15 @@ If information is missing, clearly state it in the summary.</Important>`;
1160
1363
  }
1161
1364
  if (msg.role === "tool") {
1162
1365
  formattedMessages.push({
1366
+ id: (_a = msg.metadata) == null ? void 0 : _a.id,
1163
1367
  role: "tool",
1164
1368
  content,
1165
- tool_call_id: (_a = msg.metadata) == null ? void 0 : _a.toolCallId
1369
+ name: (_b = msg.metadata) == null ? void 0 : _b.toolName,
1370
+ tool_call_id: (_c = msg.metadata) == null ? void 0 : _c.toolCallId
1166
1371
  });
1167
1372
  continue;
1168
1373
  }
1169
- const additionalKwargs = (0, import_tool_call_sanitizer.sanitizeAdditionalKwargsForToolCalls)((_b = msg.metadata) == null ? void 0 : _b.additional_kwargs, msg.toolCalls, {
1374
+ const additionalKwargs = (0, import_tool_call_sanitizer.sanitizeAdditionalKwargsForToolCalls)((_d = msg.metadata) == null ? void 0 : _d.additional_kwargs, msg.toolCalls, {
1170
1375
  onDiscard: (info) => {
1171
1376
  var _a2;
1172
1377
  this.logger.warn("Discard malformed raw tool calls from AI message", {
@@ -1415,7 +1620,22 @@ If information is missing, clearly state it in the summary.</Important>`;
1415
1620
  (0, import_middleware.toolCallStatusMiddleware)(this),
1416
1621
  ...inWorkflow ? [(0, import_middleware.workflowHistoryMiddleware)(this, this.db)] : [],
1417
1622
  (0, import_middleware.conversationMiddleware)(this, { providerName, provider, llmService, model, messageId, agentThread }),
1418
- (0, import_middleware.toolCallSanitizerMiddleware)({ logger: this.logger })
1623
+ (0, import_middleware.toolCallSanitizerMiddleware)({ logger: this.logger }),
1624
+ (0, import_middleware.toolResultIntegrityMiddleware)({
1625
+ sessionId: this.sessionId,
1626
+ logger: this.logger,
1627
+ loadToolResults: async (toolCallIds) => {
1628
+ try {
1629
+ return await this.getToolCallResults(toolCallIds);
1630
+ } catch (error) {
1631
+ this.logger.warn("Failed to load persisted tool results before model call", {
1632
+ sessionId: this.sessionId,
1633
+ error
1634
+ });
1635
+ return /* @__PURE__ */ new Map();
1636
+ }
1637
+ }
1638
+ })
1419
1639
  ];
1420
1640
  }
1421
1641
  async getCurrentThread() {
@@ -1473,6 +1693,9 @@ If information is missing, clearly state it in the summary.</Important>`;
1473
1693
  get aiMessagesRepo() {
1474
1694
  return this.ctx.db.getRepository("aiMessages");
1475
1695
  }
1696
+ get aiConversationsModel() {
1697
+ return this.ctx.db.getModel("aiConversations");
1698
+ }
1476
1699
  get aiMessagesModel() {
1477
1700
  return this.ctx.db.getModel("aiMessages");
1478
1701
  }
@@ -51,6 +51,7 @@ module.exports = __toCommonJS(ai_knowledge_base_exports);
51
51
  var import_prompts = require("@langchain/core/prompts");
52
52
  var import_ai_feature_manager = require("../manager/ai-feature-manager");
53
53
  var import_lodash = __toESM(require("lodash"));
54
+ var import_ai_employee_validation = require("../../common/ai-employee-validation");
54
55
  const KNOWLEDGE_BASE_RETRIEVAL_STRATEGIES = ["always", "onDemand"];
55
56
  const isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
56
57
  const getRoleNames = (roles) => Array.isArray(roles) ? Array.from(
@@ -120,15 +121,21 @@ class KnowledgeBaseManager {
120
121
  return "Specified knowledge base not existed";
121
122
  }
122
123
  const { knowledgeBaseKeys = [], topK, score } = employee.knowledgeBase ?? {};
123
- const promptTemplate = import_prompts.ChatPromptTemplate.fromTemplate(employee.knowledgeBasePrompt ?? "{knowledgeBaseData}");
124
+ const knowledgeBasePrompt = employee.knowledgeBasePrompt ?? "{knowledgeBaseData}";
124
125
  const docs = await this.plugin.features.knowledgeBase.search({ knowledgeBaseKeys, query, topK, score, roleNames });
125
126
  if (!(docs == null ? void 0 : docs.length)) {
126
127
  return "No document match in knowledge base";
127
128
  }
128
129
  const knowledgeBaseData = docs.map((doc) => buildKnowledgeBaseContent(doc.content, doc.metadata)).join("\n");
129
- return import_lodash.default.isEmpty(knowledgeBaseData) ? "No document match in knowledge base" : await promptTemplate.format({
130
- knowledgeBaseData
131
- });
130
+ if (import_lodash.default.isEmpty(knowledgeBaseData)) {
131
+ return "No document match in knowledge base";
132
+ }
133
+ if (!(0, import_ai_employee_validation.hasKnowledgeBaseDataPlaceholder)(knowledgeBasePrompt)) {
134
+ return `${knowledgeBasePrompt}
135
+
136
+ ${knowledgeBaseData}`;
137
+ }
138
+ return import_prompts.ChatPromptTemplate.fromTemplate(knowledgeBasePrompt).format({ knowledgeBaseData });
132
139
  }
133
140
  async hasAccessibleKnowledgeBase({ employee, roleNames }) {
134
141
  var _a;
@@ -179,31 +179,30 @@ const conversationMiddleware = (aiEmployee, options) => {
179
179
  const toolCalls = aiMessage.tool_calls;
180
180
  const values = convertAIMessage(aiMessage);
181
181
  if (values) {
182
- await aiEmployee.aiChatConversation.withTransaction(async (conversation, transaction) => {
183
- const result = await conversation.addMessages(values);
184
- newState.messageId = result.messageId;
185
- if (toolCalls == null ? void 0 : toolCalls.length) {
186
- const toolsMap = await aiEmployee.getToolsMap();
187
- const initializedToolCalls = await aiEmployee.initToolCall(
188
- transaction,
189
- result.messageId,
190
- toolCalls
191
- );
192
- fillToolCall(result, toolsMap, initializedToolCalls, toolCalls);
193
- }
182
+ const result = await aiEmployee.persistAIMessage({
183
+ values,
184
+ langChainMessageId: aiMessage.id,
185
+ toolCalls: toolCalls ?? []
194
186
  });
187
+ newState.messageId = result.message.messageId;
195
188
  if (toolCalls == null ? void 0 : toolCalls.length) {
196
- (_b = runtime.writer) == null ? void 0 : _b.call(runtime, {
197
- action: "initToolCalls",
198
- body: { toolCalls },
189
+ const toolsMap = await aiEmployee.getToolsMap();
190
+ fillToolCall(result.message, toolsMap, result.initializedToolCalls, toolCalls);
191
+ }
192
+ if (result.created) {
193
+ if (toolCalls == null ? void 0 : toolCalls.length) {
194
+ (_b = runtime.writer) == null ? void 0 : _b.call(runtime, {
195
+ action: "initToolCalls",
196
+ body: { toolCalls },
197
+ currentConversation
198
+ });
199
+ }
200
+ (_c = runtime.writer) == null ? void 0 : _c.call(runtime, {
201
+ action: "AfterAIMessageSaved",
202
+ body: { id: aiMessage.id, messageId: newState.messageId },
199
203
  currentConversation
200
204
  });
201
205
  }
202
- (_c = runtime.writer) == null ? void 0 : _c.call(runtime, {
203
- action: "AfterAIMessageSaved",
204
- body: { id: aiMessage.id, messageId: newState.messageId },
205
- currentConversation
206
- });
207
206
  }
208
207
  return newState;
209
208
  } catch (e) {
@@ -9,5 +9,6 @@
9
9
  export * from './conversation';
10
10
  export * from './skill-tools';
11
11
  export * from './tool-call-sanitizer';
12
+ export * from './tool-result-integrity';
12
13
  export * from './tools';
13
14
  export * from './workflow-history';
@@ -26,6 +26,7 @@ module.exports = __toCommonJS(middleware_exports);
26
26
  __reExport(middleware_exports, require("./conversation"), module.exports);
27
27
  __reExport(middleware_exports, require("./skill-tools"), module.exports);
28
28
  __reExport(middleware_exports, require("./tool-call-sanitizer"), module.exports);
29
+ __reExport(middleware_exports, require("./tool-result-integrity"), module.exports);
29
30
  __reExport(middleware_exports, require("./tools"), module.exports);
30
31
  __reExport(middleware_exports, require("./workflow-history"), module.exports);
31
32
  // Annotate the CommonJS export names for ESM import in node:
@@ -33,6 +34,7 @@ __reExport(middleware_exports, require("./workflow-history"), module.exports);
33
34
  ...require("./conversation"),
34
35
  ...require("./skill-tools"),
35
36
  ...require("./tool-call-sanitizer"),
37
+ ...require("./tool-result-integrity"),
36
38
  ...require("./tools"),
37
39
  ...require("./workflow-history")
38
40
  });
@@ -0,0 +1,22 @@
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 { BaseMessage } from '@langchain/core/messages';
10
+ import type { AIToolMessage } from '../../types/ai-message.type';
11
+ type ToolResultIntegrityLogger = {
12
+ warn: (message: string, meta?: Record<string, unknown>) => void;
13
+ };
14
+ export type NormalizeToolCallHistoryOptions = {
15
+ sessionId: string;
16
+ logger?: ToolResultIntegrityLogger;
17
+ loadToolResults: (toolCallIds: string[]) => Promise<Map<string, AIToolMessage>>;
18
+ };
19
+ export declare const isToolCallHistoryValid: (messages: readonly BaseMessage[]) => boolean;
20
+ export declare const normalizeToolCallHistory: (messages: readonly BaseMessage[], options: NormalizeToolCallHistoryOptions) => Promise<BaseMessage[]>;
21
+ export declare const toolResultIntegrityMiddleware: (options: NormalizeToolCallHistoryOptions) => import("langchain/dist/agents/middleware/types.cjs").AgentMiddleware<undefined, undefined, unknown, readonly (import("@langchain/core/dist/tools").ClientTool | import("@langchain/core/dist/tools").ServerTool)[]>;
22
+ export {};